Description

This notebook takes the data generated by the script in /Ex_Quartic, which describes the newly developed method, and compares it existing classification algorithms. This multi-objective problem was selected as a simple illustration, as it only takes 2 inputs and provides 2 outputs, but has the complication of a local minimum and maximum besides the Pareto optimum.

The pattern recognition algorithms are: * Support Vector Machines: example supervised learning problem * Gaussian mixture models: example unsupervised learning problem

These are compared to the process of characterizing the optimal inputs using Gaussian Processes refined by adaptive sampling.

For ease of calculation, the iterative refinement of the GP models are not included here, nor are the calculations of the conditional probabilities. The SVM and GMM methods are tested with (1) the data after finding the Pareto front (before the new adaptive sampling process), (2) the data after adaptive sampling refinement, and (3) the data after the Pareto front search with additional random sampling to see if the new sampling method is useful for these other processes as well.

Comparison among the supervised methods is done by looking at the error rate across the entire space. Since the test function is a simple polynomial, the solution of what is acceptable can be found explicitly. For the GP method, since acceptance is defined by a probability, the error rate is weighted by the probability of acceptance. For the SVM method, the error rate is simply the number of incorrectly categorized points divided by the total number of test points.

In addition to the accuracy comparison, a comparison of the importance ranking metric developed for using GP in classification problems will be compared to Shapley values. As with the accuracy comparison, this will be tested against the data before refinement, after refinement, or after a random sample.

Clear the workspace and define the functions.

# Setup
rm(list = ls())
# Visualization
library(dplyr)
library(ggplot2)
library(patchwork)
# Parallel processing
library(parallel)
library(doParallel)
# Gaussian processes
library(GPareto)
library(DiceKriging)
library(DiceOptim)
# Optimization
library(GA)
# Gaussian Mixture Models
library(mclust)
    __  ___________    __  _____________
   /  |/  / ____/ /   / / / / ___/_  __/
  / /|_/ / /   / /   / / / /\__ \ / /   
 / /  / / /___/ /___/ /_/ /___/ // /    
/_/  /_/\____/_____/\____//____//_/    version 5.4.7
Type 'citation("mclust")' for citing this R package in publications.
# Support Vector Machines
library(e1071)

Relevant functions

The quartic functions to optimize.

# Set (x1, x2) on range of [0, 5]

# Objective functions are based on the quadratic functions used previously, but with an additional local minimum
f1 = function(x1, x2){
  return(20*(x1 - 0.75)^2 + 190 + 11.58*x2^4 - 115.85*x2^3 + 383.13*x2^2 - 463.50*x2)
}
# The second objective function is also partially rotated so the local optimum is not perfectly aligned
f2 = function(x1, x2){
  # Remap both variables: rotate 30 degrees counterclockwise
  ang = -pi/24
  n1 = x1*cos(ang) - x2*sin(ang)
  n2 = x1*sin(ang) + x2*cos(ang)
  # return((x1 - 2.5)^2 + 80 + 1.778*x2^4 - 20*x2^3 + 78.573*x2^2 - 124.664*x2)
  return((n1 - 2.5)^2 + 80 + 1.778*n2^4 - 20*n2^3 + 78.573*n2^2 - 124.664*n2)
}


# Using GPareto: Need inputs and outputs as vectors/matrices not as dataframes. Coarse initial design
fun = function(x){
  x1 = x[1]; x2 = x[2]
  return(c(f1(x1, x2), f2(x1, x2)))
}

Normalization-related functions

# Normalized objective functions
n.obj = function(GPar.data, GPar.front){
  # Given dataframes that describe the entire dataset and the front, find the normalized (x,y)
  # Objective functions are named 'f1' and 'f2'
  
  # Normalize the objective outputs so that the utopia point is (0,0) and the nadir point is (1,1)
  f1.up = GPar.front$f1[which.min(GPar.front$f2)]
  f2.up = GPar.front$f2[which.min(GPar.front$f1)]
  GPar.data$f1.norm = (GPar.data$f1 - min(GPar.front$f1))/(f1.up - min(GPar.front$f1))
  GPar.data$f2.norm = (GPar.data$f2 - min(GPar.front$f2))/(f2.up - min(GPar.front$f2))

  return(GPar.data)
}

# Calculate the normalized distance
n.dist = function(f1.norm, f2.norm, GPar.front){
  # Given the normalized coordinates (f1.norm, f2.norm) and the Pareto frontier estimate,
  # find the distance along the constant f2/f1 ratio line
  
  # Determine the two points on the Pareto front that define the relevant segment
  GPar.front$theta = atan(GPar.front$f2.norm / GPar.front$f1.norm)
  if(f1.norm < 0){f1.norm = 0}
  if(f2.norm < 0){f2.norm = 0}
  ratio = atan(f2.norm/f1.norm)
  
  # Check if the angle is the same as a point on the Pareto front
  if(any(abs(ratio - GPar.front$theta) < 1e-5)){
    pos = which.min(abs(ratio - GPar.front$theta))
    Par.x = GPar.front$f1.norm[pos]
    Par.y = GPar.front$f2.norm[pos]
  } else{ # Otherwise, two points are needed for linear interpolation
    # Break the dataframe into theta above and below
    Par.above = GPar.front[GPar.front$theta - ratio > 0,]
    Par.below = GPar.front[GPar.front$theta - ratio < 0,]
    # Find the point closest to the angle
    pos.above = which.min(abs(ratio - Par.above$theta))
    pos.below = which.min(abs(ratio - Par.below$theta))
    # Linear interpolation
    ln.x = c(Par.above$f1.norm[pos.above], Par.below$f1.norm[pos.below])
    ln.y = c(Par.above$f2.norm[pos.above], Par.below$f2.norm[pos.below])
    slp = diff(ln.y)/diff(ln.x)
    # Find the point on the segment with the same angle, ie. the same ratio.
    # Solving with this constraint has analytical solution:
    Par.x = (ln.y[1] - slp*ln.x[1]) / (f2.norm/f1.norm - slp)
    Par.y = slp*(Par.x - ln.x[1]) + ln.y[1]
  }
  
  # Linear distance to the front is the difference between distances to the origin
  dist = sqrt(f1.norm^2 + f2.norm^2) - sqrt(Par.x^2 + Par.y^2)
  return(dist)
}

Gaussian process parameter tuning

fill.sample.mod = function(GPar.data, input.name, output.name){
  # Calculate the GP model to use. 
  # Using the km function, but applies checks on the system to make sure that 
  # the model uncertainty matches expectations based on GP, ie. it did not
  # fail to converge.
  
  # Based on testing, the model is bad when the 10% percentile and 90% percentile 
  # of the standard deviation are of the same order of magnitude. This is easiest
  # checked if the difference between the 10th and 90th percentile
  # is larger than the difference between the 25th and 75th.
  pt10 = 1; pt90 = 1; pt25 = 1; pt75 = 1
  while(log10(pt90/pt10) <= log10(pt75/pt25)){
    mod.out = km(design = GPar.data[, input.name], response = GPar.data[, output.name], 
                 covtyp = 'gauss', # Gaussian uncertainty
                 optim.method = 'gen', # Genetic algorithm optimization
                 control = list(trace = FALSE, # Turn off tracking to simplify output
                                pop.size = 50, # Increase robustness
                                max.generations = 400), # Some convergence issues
                 nugget = 1e-6, # Avoid eigenvalues of 0
                 )
    
    # Randomly sample 200 points from the search space
    pt = 200; i = 1
    lims = range(GPar.data[,input.name[i]])
    samp = data.frame(runif(n = pt, min = lims[1], max = lims[2]))
    for(i in 2:length(input.name)){
      lims = range(GPar.data[,input.name[i]])
      samp[,i] = runif(n = pt, min = lims[1], max = lims[2])
    }
    names(samp) = input.name
    
    # Find model output to find the percentile ranks for this iteration
    res = predict(object = mod.out, newdata = samp, type = 'UK')
    pt10 = quantile(res$sd, 0.10); pt90 = quantile(res$sd, 0.90)
    pt25 = quantile(res$sd, 0.25); pt75 = quantile(res$sd, 0.75)
  }
  return(mod.out)
}

Comparison of GP-based Boundaries with Different Acceptance Criteria

Comparison of the boundaries to show how changing the acceptance criteria changes the shape of the near-Pareto set. Showcases the robustness to different selection criteria, indicating flexibility in the design objectives.

## Loading
# Load datasets for obtaining the refined probability functions
data.delta = read.csv('../Ex_Quartic/GPar_Accept_Delta1.csv')
data.cutof = read.csv('../Ex_Quartic/GPar_Accept_Threshold.csv')
data.radan = read.csv('../Ex_Quartic/GPar_Accept_Radius.csv')
# Load datasets prior to refinement
data.paret = read.csv('../Ex_Quartic/GPar_all_start.csv')
data.paret$rad = sqrt(data.paret$f1.norm^2 + data.paret$f2.norm^2)
# Compare to the estimate of the Pareto frontier
GPar.front = read.csv(file = '../Ex_Quartic/GPar_fnt_start.csv')

# # Add a random samples to simulate the effect of sample size rather than adaptive sampling
# nsamp = max(nrow(data.delta), nrow(data.cutof), nrow(data.radan)) - nrow(data.paret)
# data.rando = data.frame(x1 = runif(n = nsamp, min = 0, max = 5),
#                         x2 = runif(n = nsamp, min = 0, max = 5))
# data.rando$f1 = f1(x1 = data.rando$x1, x2 = data.rando$x2)
# data.rando$f2 = f2(x1 = data.rando$x1, x2 = data.rando$x2)
# # Fill in the remaining calculations: normalized outputs, distance, theta, order
# data.rando = n.obj(GPar.data = data.rando, GPar.front = GPar.front)
# cl <- makeCluster(2)
# registerDoParallel(cl)
# dist = foreach(row = 1:nrow(data.rando)) %dopar%
#   n.dist(f1.norm = data.rando$f1.norm[row], f2.norm = data.rando$f2.norm[row], GPar.front = GPar.front)
# stopCluster(cl)
# data.rando$dist = unlist(dist)
# data.rando$rad = sqrt(data.rando$f1.norm^2 + data.rando$f2.norm^2)
# data.rando$theta = atan(data.rando$f2.norm/data.rando$f1.norm)*180/pi*10/9
# data.rando$order = seq(from = max(data.paret$order) + 1, to = max(data.paret$order) + nsamp, by = 1)
# data.rando = rbind(data.paret[, names(data.paret) %in% names(data.rando)], data.rando)
# 
# # Store the random data
# write.csv(data.rando, file = 'GPar_Random.csv')
data.rando = read.csv(file = 'GPar_Random.csv')

##
# Grid of the relevant region to visualize and compare
lower = c(0, 0); upper = c(5,5); grid.sz = 100
fine.grid = expand.grid(x1 = seq(from = lower[1], to = upper[1], length.out = grid.sz), 
                        x2 = seq(from = lower[2], to = upper[2], length.out = grid.sz))
fine.grid = fine.grid[,1:2]
names(fine.grid) = c('x1', 'x2')
# Calculate the actual results
fine.grid$f1 = f1(x1 = fine.grid$x1, x2 = fine.grid$x2)
fine.grid$f2 = f2(x1 = fine.grid$x1, x2 = fine.grid$x2)
fine.grid = n.obj(GPar.data = fine.grid, GPar.front = GPar.front)
fine.grid$dist = NaN
for(row in 1:nrow(fine.grid)){
  fine.grid$dist[row] = n.dist(f1.norm = fine.grid$f1.norm[row], 
                               f2.norm = fine.grid$f2.norm[row], 
                               GPar.front = GPar.front)
}
fine.grid$ang = atan(fine.grid$f2.norm/fine.grid$f1.norm)*180/pi*10/9
fine.grid$rad = sqrt(fine.grid$f1.norm^2 + fine.grid$f2.norm^2)
##
# Normalized distance
fine.grid$delta = 0
fine.grid$delta[fine.grid$dist <= 1] = 1

mod.dist.paret = fill.sample.mod(GPar.data = data.paret, input.name = c('x1', 'x2'), output.name = 'dist')
mod.dist.adapt = fill.sample.mod(GPar.data = data.delta, input.name = c('x1', 'x2'), output.name = 'dist')
mod.dist.rando = fill.sample.mod(GPar.data = data.rando, input.name = c('x1', 'x2'), output.name = 'dist')

res = predict(object = mod.dist.paret, newdata = fine.grid[,c('x1', 'x2')], type = "UK")
fine.grid$prob.delta.paret = pnorm(q = 0, mean = res$mean - 1, sd = res$sd)
res = predict(object = mod.dist.adapt, newdata = fine.grid[,c('x1', 'x2')], type = "UK")
fine.grid$prob.delta.adapt = pnorm(q = 0, mean = res$mean - 1, sd = res$sd)
res = predict(object = mod.dist.rando, newdata = fine.grid[,c('x1', 'x2')], type = "UK")
fine.grid$prob.delta.rando = pnorm(q = 0, mean = res$mean - 1, sd = res$sd)

##
# Threshold cutoff
fine.grid$cutof = 0
fine.grid$cutof[fine.grid$f1.norm <= 1 & fine.grid$f2.norm <= 1] = 1

mod.f1.paret = fill.sample.mod(GPar.data = data.paret, input.name = c('x1', 'x2'), output.name = 'f1.norm')
mod.f2.paret = fill.sample.mod(GPar.data = data.paret, input.name = c('x1', 'x2'), output.name = 'f2.norm')
mod.f1.adapt = fill.sample.mod(GPar.data = data.cutof, input.name = c('x1', 'x2'), output.name = 'f1.norm')
mod.f2.adapt = fill.sample.mod(GPar.data = data.cutof, input.name = c('x1', 'x2'), output.name = 'f2.norm')
mod.f1.rando = fill.sample.mod(GPar.data = data.rando, input.name = c('x1', 'x2'), output.name = 'f1.norm')
mod.f2.rando = fill.sample.mod(GPar.data = data.rando, input.name = c('x1', 'x2'), output.name = 'f2.norm')

res1 = predict(object = mod.f1.paret, newdata = data.frame(x1 = fine.grid$x1, x2 = fine.grid$x2), type = "UK")
res2 = predict(object = mod.f2.paret, newdata = data.frame(x1 = fine.grid$x1, x2 = fine.grid$x2), type = "UK")
fine.grid$prob.cutof.paret = pnorm(q = 0, mean = res1$mean - 1, sd = res1$sd) * 
  pnorm(q = 0, mean = res2$mean - 1, sd = res2$sd)
res1 = predict(object = mod.f1.adapt, newdata = data.frame(x1 = fine.grid$x1, x2 = fine.grid$x2), type = "UK")
res2 = predict(object = mod.f2.adapt, newdata = data.frame(x1 = fine.grid$x1, x2 = fine.grid$x2), type = "UK")
fine.grid$prob.cutof.adapt = pnorm(q = 0, mean = res1$mean - 1, sd = res1$sd) * 
  pnorm(q = 0, mean = res2$mean - 1, sd = res2$sd)
res1 = predict(object = mod.f1.rando, newdata = data.frame(x1 = fine.grid$x1, x2 = fine.grid$x2), type = "UK")
res2 = predict(object = mod.f2.rando, newdata = data.frame(x1 = fine.grid$x1, x2 = fine.grid$x2), type = "UK")
fine.grid$prob.cutof.rando = pnorm(q = 0, mean = res1$mean - 1, sd = res1$sd) * 
  pnorm(q = 0, mean = res2$mean - 1, sd = res2$sd)

##
# Radius-angle
fine.grid$radan = 0
fine.grid$radan[fine.grid$rad <= 1 & fine.grid$ang > 20] = 1

mod.rad.paret = fill.sample.mod(GPar.data = data.paret, input.name = c('x1', 'x2'), output.name = 'rad')
mod.ang.paret = fill.sample.mod(GPar.data = data.paret, input.name = c('x1', 'x2'), output.name = 'theta')
mod.rad.adapt = fill.sample.mod(GPar.data = data.radan, input.name = c('x1', 'x2'), output.name = 'rad')
mod.ang.adapt = fill.sample.mod(GPar.data = data.radan, input.name = c('x1', 'x2'), output.name = 'theta')
mod.rad.rando = fill.sample.mod(GPar.data = data.rando, input.name = c('x1', 'x2'), output.name = 'rad')
mod.ang.rando = fill.sample.mod(GPar.data = data.rando, input.name = c('x1', 'x2'), output.name = 'theta')

res1 = predict(object = mod.rad.paret, newdata = data.frame(x1 = fine.grid$x1, x2 = fine.grid$x2), type = "UK")
res2 = predict(object = mod.ang.paret, newdata = data.frame(x1 = fine.grid$x1, x2 = fine.grid$x2), type = "UK")
fine.grid$prob.radan.paret = pnorm(q = 0, mean = res1$mean - 1, sd = res1$sd) * 
  (1 - pnorm(q = 0, mean = res2$mean - 20, sd = res2$sd))
res1 = predict(object = mod.rad.adapt, newdata = data.frame(x1 = fine.grid$x1, x2 = fine.grid$x2), type = "UK")
res2 = predict(object = mod.ang.adapt, newdata = data.frame(x1 = fine.grid$x1, x2 = fine.grid$x2), type = "UK")
fine.grid$prob.radan.adapt = pnorm(q = 0, mean = res1$mean - 1, sd = res1$sd) * 
  (1 - pnorm(q = 0, mean = res2$mean - 20, sd = res2$sd))
res1 = predict(object = mod.rad.rando, newdata = data.frame(x1 = fine.grid$x1, x2 = fine.grid$x2), type = "UK")
res2 = predict(object = mod.ang.rando, newdata = data.frame(x1 = fine.grid$x1, x2 = fine.grid$x2), type = "UK")
fine.grid$prob.radan.rando = pnorm(q = 0, mean = res1$mean - 1, sd = res1$sd) * 
  (1 - pnorm(q = 0, mean = res2$mean - 20, sd = res2$sd))
sep = 0
ggplot() +
  # Boundaries: +/- some separation from 0.5
  geom_contour(data = fine.grid, mapping = aes(x = x1, y = x2, z = prob.delta.paret, color = 'delta'), 
               breaks = c(sep, -sep)+0.5) +
  geom_contour(data = fine.grid, mapping = aes(x = x1, y = x2, z = prob.cutof.paret, color = 'cutof'), 
               breaks = c(sep, -sep)+0.5) +
  geom_contour(data = fine.grid, mapping = aes(x = x1, y = x2, z = prob.radan.paret, color = 'radan'), 
               breaks = c(sep, -sep)+0.5) +
  # Pareto frontier
  geom_point(data = GPar.front, mapping = aes(x = x1, y = x2), color = 'black') + 
  geom_smooth(data = GPar.front, mapping = aes(x = x1, y = x2, color = 'Pareto'), level = 0.95, method = 'loess') + 
  labs(x = expression('x'[1]), y = expression('x'[2]), color = 'Acceptance Criteria', subtitle = 'Before Refinement') +
  scale_color_manual(values = c('delta' = 'red', 'cutof' = 'skyblue2', 'radan' = 'green', 'Pareto' = 'black'),
                     labels = c('delta' = expression(delta*' < 1'), 'cutof' = expression('F'[1]^'*'*'< 1, F'[2]^'*'*'< 1'), 
                                'radan' = expression('r < 1, '*theta*' > 18'^'o'),
                                'Pareto' = expression('Pareto Front')),
                     breaks = c('delta', 'cutof', 'radan', 'Pareto')) +
  theme_classic() + theme(legend.position = c(0.85, 0.75)) + 
  scale_x_continuous(expand = c(0, 0), limits = c(0, 5)) + scale_y_continuous(expand = c(0, 0), limits = c(0, 5)) +
  guides(colour = guide_legend(override.aes = list(fill = alpha('white', 1))))


ggplot() +
  # Boundaries: +/- some separation from 0.5
  geom_contour(data = fine.grid, mapping = aes(x = x1, y = x2, z = prob.delta.adapt, color = 'delta'), 
               breaks = c(sep, -sep)+0.5) +
  geom_contour(data = fine.grid, mapping = aes(x = x1, y = x2, z = prob.cutof.adapt, color = 'cutof'), 
               breaks = c(sep, -sep)+0.5) +
  geom_contour(data = fine.grid, mapping = aes(x = x1, y = x2, z = prob.radan.adapt, color = 'radan'), 
               breaks = c(sep, -sep)+0.5) +
  # Pareto frontier
  geom_point(data = GPar.front, mapping = aes(x = x1, y = x2), color = 'black') + 
  geom_smooth(data = GPar.front, mapping = aes(x = x1, y = x2, color = 'Pareto'), level = 0.95, method = 'loess') + 
  labs(x = expression('x'[1]), y = expression('x'[2]), color = 'Acceptance Criteria', subtitle = 'After Adaptive Sampling Refinement') +
  scale_color_manual(values = c('delta' = 'red', 'cutof' = 'skyblue2', 'radan' = 'green', 'Pareto' = 'black'),
                     labels = c('delta' = expression(delta*' < 1'), 'cutof' = expression('F'[1]^'*'*'< 1, F'[2]^'*'*'< 1'), 
                                'radan' = expression('r < 1, '*theta*' > 18'^'o'),
                                'Pareto' = expression('Pareto Front')),
                     breaks = c('delta', 'cutof', 'radan', 'Pareto')) +
  theme_classic() + theme(legend.position = c(0.85, 0.75)) + 
  scale_x_continuous(expand = c(0, 0), limits = c(0, 5)) + scale_y_continuous(expand = c(0, 0), limits = c(0, 5)) +
  guides(colour = guide_legend(override.aes = list(fill = alpha('white', 1))))


ggplot() +
  # Boundaries: +/- some separation from 0.5
  geom_contour(data = fine.grid, mapping = aes(x = x1, y = x2, z = prob.delta.rando, color = 'delta'), 
               breaks = c(sep, -sep)+0.5) +
  geom_contour(data = fine.grid, mapping = aes(x = x1, y = x2, z = prob.cutof.rando, color = 'cutof'), 
               breaks = c(sep, -sep)+0.5) +
  geom_contour(data = fine.grid, mapping = aes(x = x1, y = x2, z = prob.radan.rando, color = 'radan'), 
               breaks = c(sep, -sep)+0.5) +
  # Pareto frontier
  geom_point(data = GPar.front, mapping = aes(x = x1, y = x2), color = 'black') + 
  geom_smooth(data = GPar.front, mapping = aes(x = x1, y = x2, color = 'Pareto'), level = 0.95, method = 'loess') + 
  labs(x = expression('x'[1]), y = expression('x'[2]), color = 'Acceptance Criteria', subtitle = 'After Random Sampling') +
  scale_color_manual(values = c('delta' = 'red', 'cutof' = 'skyblue2', 'radan' = 'green', 'Pareto' = 'black'),
                     labels = c('delta' = expression(delta*' < 1'), 'cutof' = expression('F'[1]^'*'*'< 1, F'[2]^'*'*'< 1'), 
                                'radan' = expression('r < 1, '*theta*' > 18'^'o'),
                                'Pareto' = expression('Pareto Front')),
                     breaks = c('delta', 'cutof', 'radan', 'Pareto')) +
  theme_classic() + theme(legend.position = c(0.85, 0.75)) + 
  scale_x_continuous(expand = c(0, 0), limits = c(0, 5)) + scale_y_continuous(expand = c(0, 0), limits = c(0, 5)) +
  guides(colour = guide_legend(override.aes = list(fill = alpha('white', 1))))

# Plotting variables
sep = 0.05; trans = 0.25; ln.sz = 1.25

## Distance
ggplot() +
  # Boundaries: +/- some separation from 0.5
  geom_contour_filled(data = fine.grid, mapping = aes(x = x1, y = x2, z = prob.delta.paret, 
               fill = 'paret'), 
               breaks = c(sep, -sep)+0.5, linetype = 2, alpha = trans) +
  geom_contour_filled(data = fine.grid, mapping = aes(x = x1, y = x2, z = prob.delta.adapt, 
               fill = 'adapt'), 
               breaks = c(sep, -sep)+0.5, linetype = 2, alpha = trans) +
  geom_contour_filled(data = fine.grid, mapping = aes(x = x1, y = x2, z = prob.delta.rando, 
               fill = 'rando'), 
               breaks = c(sep, -sep)+0.5, linetype = 2, alpha = trans) +
  # Actual boundaries
  geom_contour(data = fine.grid, mapping = aes(x = x1, y = x2, z = prob.delta.paret, color = 'paret'), 
               breaks = 0.5, linetype = 1, size = ln.sz) +
  geom_contour(data = fine.grid, mapping = aes(x = x1, y = x2, z = prob.delta.adapt, color = 'adapt'), 
               breaks = 0.5, linetype = 1, size = ln.sz) +
  geom_contour(data = fine.grid, mapping = aes(x = x1, y = x2, z = prob.delta.rando, color = 'rando'), 
               breaks = 0.5, linetype = 1, size = ln.sz) +
  geom_contour(data = fine.grid, mapping = aes(x = x1, y = x2, z = delta, color = 'tru'), 
               breaks = 0.5, linetype = 1, size = ln.sz) +
  scale_color_manual(breaks = c('tru', 'paret', 'adapt', 'rando'),
                     labels = c('tru' = 'True Boundary', 'paret' = 'Starting Dataset', 
                                'adapt' = '+ Adaptive Sampling', 'rando' = '+ Random Sampling'),
                     values = c('tru' = 'black', 'paret' = 'skyblue2', 'adapt' = 'red', 'rando' = 'green')) +
  scale_fill_manual(breaks = c('tru', 'paret', 'adapt', 'rando'),
                     labels = c('tru' = 'True Boundary', 'paret' = 'Starting Dataset', 
                                'adapt' = '+ Adaptive Sampling', 'rando' = '+ Random Sampling'),
                     values = c('tru' = 'black', 'paret' = 'skyblue2', 'adapt' = 'red', 'rando' = 'green')) +
  labs(x = expression('x'[1]), x = expression('x'[2]), subtitle = 'Criteria: Pareto Distance', color = '') +
  theme_classic() + guides(fill = FALSE) + theme(legend.position = c(0.85, 0.85)) +
  scale_x_continuous(limits = c(0, 5), expand = c(0, 0)) + scale_y_continuous(limits = c(0, 5), expand = c(0, 0))


## Threshold
ggplot() +
  # Boundaries: +/- some separation from 0.5
  geom_contour_filled(data = fine.grid, mapping = aes(x = x1, y = x2, z = prob.cutof.paret, 
               fill = 'paret'), 
               breaks = c(sep, -sep)+0.5, linetype = 2, alpha = trans) +
  geom_contour_filled(data = fine.grid, mapping = aes(x = x1, y = x2, z = prob.cutof.adapt, 
               fill = 'adapt'), 
               breaks = c(sep, -sep)+0.5, linetype = 2, alpha = trans) +
  geom_contour_filled(data = fine.grid, mapping = aes(x = x1, y = x2, z = prob.cutof.rando, 
               fill = 'rando'), 
               breaks = c(sep, -sep)+0.5, linetype = 2, alpha = trans) +
  # Actual boundaries
  geom_contour(data = fine.grid, mapping = aes(x = x1, y = x2, z = prob.cutof.paret, color = 'paret'), 
               breaks = 0.5, linetype = 1, size = ln.sz) +
  geom_contour(data = fine.grid, mapping = aes(x = x1, y = x2, z = prob.cutof.adapt, color = 'adapt'), 
               breaks = 0.5, linetype = 1, size = ln.sz) +
  geom_contour(data = fine.grid, mapping = aes(x = x1, y = x2, z = prob.cutof.rando, color = 'rando'), 
               breaks = 0.5, linetype = 1, size = ln.sz) +
  geom_contour(data = fine.grid, mapping = aes(x = x1, y = x2, z = cutof, color = 'tru'), 
               breaks = 0.5, linetype = 1, size = ln.sz) +
  scale_color_manual(breaks = c('tru', 'paret', 'adapt', 'rando'),
                     labels = c('tru' = 'True Boundary', 'paret' = 'Starting Dataset', 
                                'adapt' = '+ Adaptive Sampling', 'rando' = '+ Random Sampling'),
                     values = c('tru' = 'black', 'paret' = 'skyblue2', 'adapt' = 'red', 'rando' = 'green')) +
  scale_fill_manual(breaks = c('tru', 'paret', 'adapt', 'rando'),
                     labels = c('tru' = 'True Boundary', 'paret' = 'Starting Dataset', 
                                'adapt' = '+ Adaptive Sampling', 'rando' = '+ Random Sampling'),
                     values = c('tru' = 'black', 'paret' = 'skyblue2', 'adapt' = 'red', 'rando' = 'green')) +
  labs(x = expression('x'[1]), x = expression('x'[2]), subtitle = 'Criteria: Objective function values', color = '') +
  theme_classic() + guides(fill = FALSE) + theme(legend.position = c(0.85, 0.85)) +
  scale_x_continuous(limits = c(0, 5), expand = c(0, 0)) + scale_y_continuous(limits = c(0, 5), expand = c(0, 0))


## Radius-angle
ggplot() +
  # Boundaries: +/- some separation from 0.5
  geom_contour_filled(data = fine.grid, mapping = aes(x = x1, y = x2, z = prob.radan.paret, 
               fill = 'paret'), 
               breaks = c(sep, -sep)+0.5, linetype = 2, alpha = trans) +
  geom_contour_filled(data = fine.grid, mapping = aes(x = x1, y = x2, z = prob.radan.adapt, 
               fill = 'adapt'), 
               breaks = c(sep, -sep)+0.5, linetype = 2, alpha = trans) +
  geom_contour_filled(data = fine.grid, mapping = aes(x = x1, y = x2, z = prob.radan.rando, 
               fill = 'rando'), 
               breaks = c(sep, -sep)+0.5, linetype = 2, alpha = trans) +
  # Actual boundaries
  geom_contour(data = fine.grid, mapping = aes(x = x1, y = x2, z = prob.radan.paret, color = 'paret'), 
               breaks = 0.5, linetype = 1, size = ln.sz) +
  geom_contour(data = fine.grid, mapping = aes(x = x1, y = x2, z = prob.radan.adapt, color = 'adapt'), 
               breaks = 0.5, linetype = 1, size = ln.sz) +
  geom_contour(data = fine.grid, mapping = aes(x = x1, y = x2, z = prob.radan.rando, color = 'rando'), 
               breaks = 0.5, linetype = 1, size = ln.sz) +
  geom_contour(data = fine.grid, mapping = aes(x = x1, y = x2, z = radan, color = 'tru'), 
               breaks = 0.5, linetype = 1, size = ln.sz) +
  scale_color_manual(breaks = c('tru', 'paret', 'adapt', 'rando'),
                     labels = c('tru' = 'True Boundary', 'paret' = 'Starting Dataset', 
                                'adapt' = '+ Adaptive Sampling', 'rando' = '+ Random Sampling'),
                     values = c('tru' = 'black', 'paret' = 'skyblue2', 'adapt' = 'red', 'rando' = 'green')) +
  scale_fill_manual(breaks = c('tru', 'paret', 'adapt', 'rando'),
                     labels = c('tru' = 'True Boundary', 'paret' = 'Starting Dataset', 
                                'adapt' = '+ Adaptive Sampling', 'rando' = '+ Random Sampling'),
                     values = c('tru' = 'black', 'paret' = 'skyblue2', 'adapt' = 'red', 'rando' = 'green')) +
  labs(x = expression('x'[1]), x = expression('x'[2]), color = '',
       subtitle = expression('Criteria: Utopia point distance and f'[1]*' Priority > 80%')) +
  theme_classic() + guides(fill = FALSE) + theme(legend.position = c(0.85, 0.85)) +
  scale_x_continuous(limits = c(0, 5), expand = c(0, 0)) + scale_y_continuous(limits = c(0, 5), expand = c(0, 0))

NA
NA

The error rate in the GP model estimate should account for the built-in uncertainty in the model. The probability that the model gives the wrong result given the \((x_1, x_2)\) coordinates is the probability that it accepts the point when it should reject it, or vice versa. Since all \((x_1, x_2)\) are equally likely in this mathematical function, the error rate of the GP model is the average of these probabilities. There should be an evident decrease in the error rate between the pre- and post-refinement models.

The Monte Carlo uncertainty in the error rate is \(\sqrt{\frac{p(1-p)}{N}}\).

Since the fine grid calculation was already calculated, using that instead of a fully random assignment.

Also include the rate of type I (P[should reject | accepted]) and type II error (P[should accept | rejected]) error rates.

By Bayes rule, \(P[X|Y] = P[X, Y] P[X] / P[Y]\)

error.rate = data.frame(method = 'GP', source = rep(c('0paret', '1adapt', '2rando'), 3),
                        criteria = c(rep('Pareto Distance', 3), 
                                     rep('Objective Cutoff', 3), 
                                     rep('Utopia Distance + Priority', 3)))
error.rate$criteria = factor(error.rate$criteria, levels = c("Pareto Distance", 
                                     "Objective Cutoff", "Utopia Distance + Priority"))
# Total error rate
error.rate$rate =
  c(mean(c(1 - filter(fine.grid, delta == 1)$prob.delta.paret, filter(fine.grid, delta == 0)$prob.delta.paret)),
    mean(c(1 - filter(fine.grid, delta == 1)$prob.delta.adapt, filter(fine.grid, delta == 0)$prob.delta.adapt)),
    mean(c(1 - filter(fine.grid, delta == 1)$prob.delta.rando, filter(fine.grid, delta == 0)$prob.delta.rando)),
    mean(c(1 - filter(fine.grid, cutof == 1)$prob.cutof.paret, filter(fine.grid, cutof == 0)$prob.cutof.paret)),
    mean(c(1 - filter(fine.grid, cutof == 1)$prob.cutof.adapt, filter(fine.grid, cutof == 0)$prob.cutof.adapt)),
    mean(c(1 - filter(fine.grid, cutof == 1)$prob.cutof.rando, filter(fine.grid, cutof == 0)$prob.cutof.rando)),
    mean(c(1 - filter(fine.grid, radan == 1)$prob.radan.paret, filter(fine.grid, radan == 0)$prob.radan.paret)),
    mean(c(1 - filter(fine.grid, radan == 1)$prob.radan.adapt, filter(fine.grid, radan == 0)$prob.radan.adapt)),
    mean(c(1 - filter(fine.grid, radan == 1)$prob.radan.rando, filter(fine.grid, radan == 0)$prob.radan.rando)))
error.rate$err = sqrt(error.rate$rate*(1-error.rate$rate)/nrow(fine.grid))

# Type 2 error = P[should accept | rejected]
error.rate$typ2 =
  c(sum(1 - filter(fine.grid, delta == 1)$prob.delta.paret)*nrow(filter(fine.grid, delta == 1))/nrow(fine.grid)^2/
      mean(1 - fine.grid$prob.delta.paret),
    sum(1 - filter(fine.grid, delta == 1)$prob.delta.adapt)*nrow(filter(fine.grid, delta == 1))/nrow(fine.grid)^2/
      mean(1 - fine.grid$prob.delta.adapt),
    sum(1 - filter(fine.grid, delta == 1)$prob.delta.rando)*nrow(filter(fine.grid, delta == 1))/nrow(fine.grid)^2/
      mean(1 - fine.grid$prob.delta.rando),
    sum(1 - filter(fine.grid, cutof == 1)$prob.cutof.paret)*nrow(filter(fine.grid, cutof == 1))/nrow(fine.grid)^2/
      mean(1 - fine.grid$prob.cutof.paret),
    sum(1 - filter(fine.grid, cutof == 1)$prob.cutof.adapt)*nrow(filter(fine.grid, cutof == 1))/nrow(fine.grid)^2/
      mean(1 - fine.grid$prob.cutof.adapt),
    sum(1 - filter(fine.grid, cutof == 1)$prob.cutof.rando)*nrow(filter(fine.grid, cutof == 1))/nrow(fine.grid)^2/
      mean(1 - fine.grid$prob.cutof.rando),
    sum(1 - filter(fine.grid, radan == 1)$prob.radan.paret)*nrow(filter(fine.grid, radan == 1))/nrow(fine.grid)^2/
      mean(1 - fine.grid$prob.radan.paret),
    sum(1 - filter(fine.grid, radan == 1)$prob.radan.adapt)*nrow(filter(fine.grid, radan == 1))/nrow(fine.grid)^2/
      mean(1 - fine.grid$prob.radan.adapt),
    sum(1 - filter(fine.grid, radan == 1)$prob.radan.rando)*nrow(filter(fine.grid, radan == 1))/nrow(fine.grid)^2/
      mean(1 - fine.grid$prob.radan.rando))
error.rate$typ2.err = sqrt(error.rate$typ2*(1-error.rate$typ2) / nrow(fine.grid))
                       # c(rep(nrow(filter(fine.grid, delta == 0)), 3),
                       #   rep(nrow(filter(fine.grid, cutof == 0)), 3),
                       #   rep(nrow(filter(fine.grid, radan == 0)), 3)))

# Type 1 error = P[should reject | accepted]
error.rate$typ1 =
  c(sum(filter(fine.grid, delta == 0)$prob.delta.paret)*nrow(filter(fine.grid, delta == 0))/nrow(fine.grid)^2/
      mean(fine.grid$prob.delta.paret),
    sum(filter(fine.grid, delta == 0)$prob.delta.adapt)*nrow(filter(fine.grid, delta == 0))/nrow(fine.grid)^2/
      mean(fine.grid$prob.delta.adapt),
    sum(filter(fine.grid, delta == 0)$prob.delta.rando)*nrow(filter(fine.grid, delta == 0))/nrow(fine.grid)^2/
      mean(fine.grid$prob.delta.rando),
    sum(filter(fine.grid, cutof == 0)$prob.cutof.paret)*nrow(filter(fine.grid, cutof == 0))/nrow(fine.grid)^2/
      mean(fine.grid$prob.cutof.paret),
    sum(filter(fine.grid, cutof == 0)$prob.cutof.adapt)*nrow(filter(fine.grid, cutof == 0))/nrow(fine.grid)^2/
      mean(fine.grid$prob.cutof.adapt),
    sum(filter(fine.grid, cutof == 0)$prob.cutof.rando)*nrow(filter(fine.grid, cutof == 0))/nrow(fine.grid)^2/
      mean(fine.grid$prob.cutof.rando),
    sum(filter(fine.grid, radan == 0)$prob.radan.paret)*nrow(filter(fine.grid, radan == 0))/nrow(fine.grid)^2/
      mean(fine.grid$prob.radan.paret),
    sum(filter(fine.grid, radan == 0)$prob.radan.adapt)*nrow(filter(fine.grid, radan == 0))/nrow(fine.grid)^2/
      mean(fine.grid$prob.radan.adapt),
    sum(filter(fine.grid, radan == 0)$prob.radan.rando)*nrow(filter(fine.grid, radan == 0))/nrow(fine.grid)^2/
      mean(fine.grid$prob.radan.rando))
error.rate$typ1.err = sqrt(error.rate$typ1*(1-error.rate$typ1) / nrow(fine.grid))
                       # c(rep(nrow(filter(fine.grid, delta == 0)), 3),
                       #   rep(nrow(filter(fine.grid, cutof == 0)), 3),
                       #   rep(nrow(filter(fine.grid, radan == 0)), 3)))
error.rate

g.tot = ggplot(error.rate) +
  geom_col(mapping = aes(x = source, y = rate, fill = source)) +
  geom_errorbar(mapping = aes(x = source, y = rate, ymin = rate - err, ymax = rate + err), width = 0.5) +
  facet_wrap(~criteria, scales = 'free') +
  scale_x_discrete(breaks = c(), labels = c()) +
  labs(x = '', y = 'Total\nError Rate') +
  scale_fill_manual(labels = c('0paret' = 'Starting Dataset', '1adapt' = '+ Adaptive Sampling', 
                                 '2rando' = '+ Random Sampling'), name = '',
                      values = c('0paret' = 'skyblue2', '1adapt' = 'red', '2rando' = 'green')) +
  theme_classic() + scale_y_continuous(expand = expansion(mult = c(0, .1)))
g.ty1 = ggplot(error.rate) +
  geom_col(mapping = aes(x = source, y = typ1, fill = source)) +
  geom_errorbar(mapping = aes(x = source, y = typ1, ymin = typ1 - typ1.err, ymax = typ1 + typ1.err), width = 0.5) +
  facet_wrap(~criteria, scales = 'free') +
  scale_x_discrete(breaks = c(), labels = c()) +
  labs(x = '', y = 'False Positive\nError Rate') +
  scale_fill_manual(labels = c('0paret' = 'Starting Dataset', '1adapt' = '+ Adaptive Sampling', 
                                 '2rando' = '+ Random Sampling'), name = '',
                      values = c('0paret' = 'skyblue2', '1adapt' = 'red', '2rando' = 'green')) +
  theme_classic() + scale_y_continuous(expand = expansion(mult = c(0, .1)))
g.ty2 = ggplot(error.rate) +
  geom_col(mapping = aes(x = source, y = typ2, fill = source)) +
  geom_errorbar(mapping = aes(x = source, y = typ2, ymin = typ2 - typ2.err, ymax = typ2 + typ2.err), width = 0.5) +
  facet_wrap(~criteria, scales = 'free') +
  scale_x_discrete(breaks = c(), labels = c()) +
  labs(x = '', y = 'False Negative\nError Rate') +
  scale_fill_manual(labels = c('0paret' = 'Starting Dataset', '1adapt' = '+ Adaptive Sampling', 
                                 '2rando' = '+ Random Sampling'), name = '',
                      values = c('0paret' = 'skyblue2', '1adapt' = 'red', '2rando' = 'green')) +
  theme_classic() + scale_y_continuous(expand = expansion(mult = c(0, .1)))

(g.tot + guides(fill = FALSE)) / g.ty1 / (g.ty2 + guides(fill = FALSE))


write.csv(error.rate, 'ErrorRates.csv')
rm(g.tot, g.ty1, g.ty2)

A closer inspection of each GP-discovered boundary compared to the actual boundary based on fine-resolution function evaluation.

# Single variable conditionals compared to the expected conditionals by integration
Infer.plt = read.csv('../Ex_Quartic/Marginals_delta.csv')
ggplot() +
  geom_path(data = filter(Infer.plt, cat == 'tru'), mapping = aes(x = x, y = prob - psd, color = cat), linetype = 2) +
  geom_path(data = filter(Infer.plt, cat == 'tru'), mapping = aes(x = x, y = prob + psd, color = cat), linetype = 2) +
  geom_path(data = filter(Infer.plt, cat != 'tru'), mapping = aes(x = x, y = prob, color = cat)) +
  facet_wrap(var~., nrow = 2) + 
  scale_color_manual(labels = c('tru' = 'Expected Marginal', 'start' = 'Starting Dataset', 
                                  'adapt' = '+ Adaptive Sampling', 'rando' = '+ Random Sampling'),
                     values = c('tru' = 'black', 'start' = 'skyblue2', 
                                  'adapt' = 'red', 'rando' = 'green'),
                     breaks = c('tru', 'start', 'adapt', 'rando')) +
  guides(color = guide_legend(override.aes = list(linetype = c(2, 1, 1, 1)))) +
  labs(x = '', y = 'Probability of Acceptance', subtitle = expression('Pareto Distance'), color = '') +
  scale_y_continuous(expand = c(0, 0.05)) + scale_x_continuous(expand = c(0, 0)) +
  theme_bw()


Infer.plt = read.csv('../Ex_Quartic/Marginals_cutof.csv')
ggplot() +
  geom_path(data = filter(Infer.plt, cat == 'tru'), mapping = aes(x = x, y = prob - psd, color = cat), linetype = 2) +
  geom_path(data = filter(Infer.plt, cat == 'tru'), mapping = aes(x = x, y = prob + psd, color = cat), linetype = 2) +
  geom_path(data = filter(Infer.plt, cat != 'tru'), mapping = aes(x = x, y = prob, color = cat)) +
  facet_wrap(var~., nrow = 2) + 
  scale_color_manual(labels = c('tru' = 'Expected Marginal', 'start' = 'Starting Dataset', 
                                  'adapt' = '+ Adaptive Sampling', 'rando' = '+ Random Sampling'),
                     values = c('tru' = 'black', 'start' = 'skyblue2', 
                                  'adapt' = 'red', 'rando' = 'green'),
                     breaks = c('tru', 'start', 'adapt', 'rando')) +
  guides(color = guide_legend(override.aes = list(linetype = c(2, 1, 1, 1)))) +
  labs(x = '', y = 'Probability of Acceptance', subtitle = expression('Cutoff Thresholds'), color = '') +
  scale_y_continuous(expand = c(0, 0.05)) + scale_x_continuous(expand = c(0, 0)) +
  theme_bw()


Infer.plt = read.csv('../Ex_Quartic/Marginals_radan.csv')
ggplot() +
  geom_path(data = filter(Infer.plt, cat == 'tru'), mapping = aes(x = x, y = prob - psd, color = cat), linetype = 2) +
  geom_path(data = filter(Infer.plt, cat == 'tru'), mapping = aes(x = x, y = prob + psd, color = cat), linetype = 2) +
  geom_path(data = filter(Infer.plt, cat != 'tru'), mapping = aes(x = x, y = prob, color = cat)) +
  facet_wrap(var~., nrow = 2, labeller = label_parsed) + 
  scale_color_manual(labels = c('tru' = 'Expected Marginal', 'start' = 'Starting Dataset', 
                                  'adapt' = '+ Adaptive Sampling', 'rando' = '+ Random Sampling'),
                     values = c('tru' = 'black', 'start' = 'skyblue2', 
                                  'adapt' = 'red', 'rando' = 'green'),
                     breaks = c('tru', 'start', 'adapt', 'rando')) +
  guides(color = guide_legend(override.aes = list(linetype = c(2, 1, 1, 1)))) +
  labs(x = '', y = 'Probability of Acceptance', subtitle = expression('Utopia Distance + Priority'), color = '') +
  scale_y_continuous(expand = c(0, 0.05)) + scale_x_continuous(expand = c(0, 0)) +
  theme_bw()

NA
NA

Existing Method: Support Vector Machines

The method should use only the data from the initial Pareto front calculation. Additional samples may be necessary to refine the results, but there is no established method for this refinement as of yet.

SVM methods require existing labels for the points; in this case, the labels are whether or not the point meets the same three selection criteria as tested with the new GP method.

Sigmoidal kernels always give very bad fits, so ignoring those possibilities in the error calculation.

# Create a function to output the plot of all 4 models compared to the real result
SVM.input = function(fine.input, mod.rad, mod.lin, mod.pol, mod.sig, tit){
  # Fine input has the variables x1, x2, and cat
  res = predict(object = mod.lin, newdata = fine.input[, c('x1', 'x2')])
  fine.input$res = res
  g.lin = ggplot(fine.input) +
    geom_point(data = fine.input, mapping = aes(x = x1, y = x2, color = res)) +
    geom_contour(data = fine.input, mapping = aes(x = x1, y = x2, z = cat), breaks = c(0.5)) +
    labs(subtitle = 'Linear', x = expression('x'[1]), y = expression('x'[2])) + 
    guides(color = FALSE)
  res = predict(object = mod.rad, newdata = fine.input[,c('x1', 'x2')])
  fine.input$res = res
  g.rad = ggplot() +
    geom_point(data = fine.input, mapping = aes(x = x1, y = x2, color = res)) +
    geom_contour(data = fine.input, mapping = aes(x = x1, y = x2, z = cat), breaks = c(0.5)) +
    labs(subtitle = 'Radial', x = expression('x'[1]), y = expression('x'[2]), title = tit) + guides(color = FALSE)
  res = predict(object = mod.pol, newdata = fine.input[,c('x1', 'x2')])
  fine.input$res = res
  g.pol = ggplot() +
    geom_point(data = fine.input, mapping = aes(x = x1, y = x2, color = res)) +
    geom_contour(data = fine.input, mapping = aes(x = x1, y = x2, z = cat), breaks = c(0.5)) +
    labs(subtitle = 'Polynomial', x = expression('x'[1]), y = expression('x'[2])) + guides(color = FALSE)
  res = predict(object = mod.sig, newdata = fine.input[,c('x1', 'x2')])
  fine.input$res = res
  g.sig = ggplot() +
    geom_point(data = fine.input, mapping = aes(x = x1, y = x2, color = res)) +
    geom_contour(data = fine.input, mapping = aes(x = x1, y = x2, z = cat), breaks = c(0.5)) +
    labs(subtitle = 'Sigmoid', x = expression('x'[1]), y = expression('x'[2])) + guides(color = FALSE)
  return((g.rad + g.lin) / (g.pol + g.sig))
}

SVM.mod.all = function(input.data){
  # Given input data, find all 4 default models and return as a list
  fit.rad = svm(as.factor(cat) ~ x1*x2, data = input.data[,c('x1', 'x2', 'cat')], 
            scale = FALSE, kernel = "radial", cost = 5)
  fit.lin = svm(as.factor(cat) ~ x1*x2, data = input.data[,c('x1', 'x2', 'cat')], 
            scale = FALSE, kernel = "linear", cost = 5)
  # Polynomial: increase the kernel degree due to complexity of the boundary; maximum tested that still achieved convergence
  fit.pol = svm(as.factor(cat) ~ x1*x2, data = input.data[,c('x1', 'x2', 'cat')], 
            scale = FALSE, kernel = "polynomial", cost = 5, degree = 3.5)
  # Increease the coefficient
  fit.sig = svm(as.factor(cat) ~ x1*x2, data = input.data[,c('x1', 'x2', 'cat')], 
            scale = FALSE, kernel = "sigmoid", cost = 5)
  return(list(rad = fit.rad, lin = fit.lin, pol = fit.pol, sig = fit.sig))
}
SVM.err = function(fine.input, mod.list){
  # Fine grid has the real result; mod.list is the 4 different SVM kernels
  fine.input$rad = predict(object = mod.list$rad, newdata = fine.input[,c('x1', 'x2')])
  fine.input$lin = predict(object = mod.list$lin, newdata = fine.input[,c('x1', 'x2')])
  fine.input$pol = predict(object = mod.list$pol, newdata = fine.input[,c('x1', 'x2')])
  # fine.input$sig = predict(object = mod.list$sig, newdata = fine.input[,c('x1', 'x2')])
  
  # Error rate
  rad = nrow(filter(fine.input, cat == 0, rad == 1)) + nrow(filter(fine.input, cat == 1, rad == 0))
  lin = nrow(filter(fine.input, cat == 0, lin == 1)) + nrow(filter(fine.input, cat == 1, lin == 0))
  pol = nrow(filter(fine.input, cat == 0, pol == 1)) + nrow(filter(fine.input, cat == 1, pol == 0))
  # sig = nrow(filter(fine.input, cat == 0, sig == 1)) + nrow(filter(fine.input, cat == 1, sig == 0))
  # rate = c(rad, lin, pol, sig)/nrow(fine.input)
  rate = c(rad, lin, pol)/nrow(fine.input)
  # Uncertainty
  err = sqrt(rate*(1-rate)/nrow(fine.input))
  
  # Type I error = P[should reject | accepted] = P[both] P[should reject] / P[accepted]
  typ1 =
    c(nrow(filter(fine.input, cat == 0, rad == 1))*nrow(filter(fine.input, cat == 0))/nrow(fine.input)/
        nrow(filter(fine.input, rad == 1)),
      nrow(filter(fine.input, cat == 0, lin == 1))*nrow(filter(fine.input, cat == 0))/nrow(fine.input)/
        nrow(filter(fine.input, lin == 1)),
      nrow(filter(fine.input, cat == 0, pol == 1))*nrow(filter(fine.input, cat == 0))/nrow(fine.input)/
        nrow(filter(fine.input, pol == 1)))#,
      # nrow(filter(fine.input, cat == 0, sig == 1))*nrow(filter(fine.input, cat == 0))/nrow(fine.input)/
        # nrow(filter(fine.input, sig == 1)) )
  typ1.err = sqrt(typ1*(1-typ1) / nrow(fine.input))  
  
  # Type II error = P[should accept | rejected] = P[both] P[should accept] / P[rejected]
  typ2 =
    c(nrow(filter(fine.input, cat == 1, rad == 0))*nrow(filter(fine.input, cat == 1))/nrow(fine.input)/
        nrow(filter(fine.input, rad == 0)),
      nrow(filter(fine.input, cat == 1, lin == 0))*nrow(filter(fine.input, cat == 1))/nrow(fine.input)/
        nrow(filter(fine.input, lin == 0)),
      nrow(filter(fine.input, cat == 1, pol == 0))*nrow(filter(fine.input, cat == 1))/nrow(fine.input)/
        nrow(filter(fine.input, pol == 0)))#,
      # nrow(filter(fine.input, cat == 1, sig == 0))*nrow(filter(fine.input, cat == 1))/nrow(fine.input)/
        # nrow(filter(fine.input, sig == 0)) )
  typ2.err = sqrt(typ2*(1-typ2) / nrow(fine.input))
  # return(data.frame(method = c('SVM-rad', 'SVM-lin', 'SVM-pol', 'SVM-sig'), rate, err, typ1, typ1.err, typ2, typ2.err))
  return(data.frame(method = c('SVM-rad', 'SVM-lin', 'SVM-pol'), rate, err, typ1, typ1.err, typ2, typ2.err))
}

# For the radial and polynomial kernels, calculate the marginals and Shapley values for comparison
marginal = function(mod){
  x.rng = seq(from = 0, to = 5, length.out = 50)
  svm.margin = data.frame()
  nsamp = 2000
  for(x in x.rng){
    # x1 marginal
    temp.frame = data.frame(x1 = x, x2 = runif(n = nsamp, min = 0, max = 5))
    res = as.numeric(predict(object = mod$pol, newdata = temp.frame))-1
    svm.margin = rbind(svm.margin, data.frame(x = x, var = 'x1', prob = sum(res)/length(res), method = 'SVM-pol'))
    res = as.numeric(predict(object = mod$rad, newdata = temp.frame))-1
    svm.margin = rbind(svm.margin, data.frame(x = x, var = 'x1', prob = sum(res)/length(res), method = 'SVM-rad'))
    # x1 marginal
    temp.frame = data.frame(x2 = x, x1 = runif(n = nsamp, min = 0, max = 5))
    res = as.numeric(predict(object = mod$pol, newdata = temp.frame))-1
    svm.margin = rbind(svm.margin, data.frame(x = x, var = 'x2', prob = sum(res)/length(res), method = 'SVM-pol'))
    res = as.numeric(predict(object = mod$rad, newdata = temp.frame))-1
    svm.margin = rbind(svm.margin, data.frame(x = x, var = 'x2', prob = sum(res)/length(res), method = 'SVM-rad'))
  }
  
  svm.margin$psd = sqrt(svm.margin$prob*(1 - svm.margin$prob)/nsamp)
  return(svm.margin)
}

SVM.shap = function(mod){
  # Strumbelj et al. (2014) Monte Carlo estimate.
  # Since this is a classification setting, only a difference of 0 or 1 is possible 
  # i.e. a difference of -1 is the same as a difference of 1, as it is simply a misclassification
  nsamp = 1500*50 # Same number of samples as other methods for consistency
  x0 = data.frame(x1 = runif(n = nsamp, min = 0, max = 5),
                  x2 = runif(n = nsamp, min = 0, max = 5))
  z1 = data.frame(x1 = x0$x1,
                  x2 = runif(n = nsamp, min = 0, max = 5))
  z2 = data.frame(x1 = runif(n = nsamp, min = 0, max = 5),
                  x2 = x0$x2)
  # Apply functions to all
  res.pol = as.numeric(predict(object = mod$pol, newdata = x0))
  res.rad = as.numeric(predict(object = mod$rad, newdata = x0))
  x0$pol = res.pol; x0$rad = res.rad
  res.pol = as.numeric(predict(object = mod$pol, newdata = z1))
  res.rad = as.numeric(predict(object = mod$rad, newdata = z1))
  z1$pol = res.pol; z1$rad = res.rad
  res.pol = as.numeric(predict(object = mod$pol, newdata = z2))
  res.rad = as.numeric(predict(object = mod$rad, newdata = z2))
  z2$pol = res.pol; z2$rad = res.rad
  # Calculate mean and standard error of the differences for Shapley values
  shap = c(mean((x0$pol - z1$pol)), mean((x0$pol - z2$pol)),
           mean((x0$rad - z1$rad)), mean((x0$rad - z2$rad)))
  r.shap = c(shap[1:2]/max(shap[1:2]), shap[3:4]/max(shap[3:4]))
  # For standard error, calculate standard error of the mean with n = 1500
  # to be consistent with the other standard errors
  pol.x1m = apply(matrix(x0$pol - z1$pol, nrow = 1500), 2, mean)
  rad.x1m = apply(matrix(x0$rad - z1$rad, nrow = 1500), 2, mean)
  pol.x2m = apply(matrix(x0$pol - z2$pol, nrow = 1500), 2, mean)
  rad.x2m = apply(matrix(x0$rad - z2$rad, nrow = 1500), 2, mean)
  
  shap.err = c(sd(pol.x1m), sd(pol.x2m),
               sd(rad.x1m), sd(rad.x2m))
  return(data.frame(import = shap, var = c('x1', 'x2'), 
                    sd = shap.err, r.import = r.shap, 
                    method = c('Shapley-pol', 'Shapley-pol', 
                               'Shapley-rad', 'Shapley-rad')))
}
data.paret = read.csv('../Ex_Quartic/GPar_all_start.csv')
data.delta = read.csv('../Ex_Quartic/GPar_Accept_Delta1.csv')
data.rando = read.csv(file = 'GPar_Random.csv')

# Define acceptance
data.paret$cat = 0; data.delta$cat = 0; data.rando$cat = 0; fine.grid$cat = 0
data.paret$cat[data.paret$dist <= 1] = 1
data.delta$cat[data.delta$dist <= 1] = 1
data.rando$cat[data.rando$dist <= 1] = 1
fine.grid$cat[fine.grid$dist <= 1] = 1

# Plots
mod.paret = SVM.mod.all(input.data = data.paret)
SVM.input(fine.input = fine.grid[,c('x1', 'x2', 'cat')], 
          mod.rad = mod.paret$rad, mod.lin = mod.paret$lin, 
          mod.pol = mod.paret$pol, mod.sig = mod.paret$sig,
          tit = 'Pareto Distance, Starting Dataset')


mod.adapt = SVM.mod.all(input.data = data.delta)
# Store this model for importance ranking later
mod.adapt.delta = mod.adapt
SVM.input(fine.input = fine.grid[,c('x1', 'x2', 'cat')], 
          mod.rad = mod.adapt$rad, mod.lin = mod.adapt$lin, 
          mod.pol = mod.adapt$pol, mod.sig = mod.adapt$sig,
          tit = 'Pareto Distance, + Adaptive Sampling')


mod.rando = SVM.mod.all(input.data = data.rando)
SVM.input(fine.input = fine.grid[,c('x1', 'x2', 'cat')], 
          mod.rad = mod.rando$rad, mod.lin = mod.rando$lin, 
          mod.pol = mod.rando$pol, mod.sig = mod.rando$sig,
          tit = 'Pareto Distance, + Random Sampling')


err.paret = SVM.err(fine.input = fine.grid, mod.list = mod.paret)
err.paret$source = '0paret'; err.paret$criteria = 'Pareto Distance'
err.adapt = SVM.err(fine.input = fine.grid, mod.list = mod.adapt)
err.adapt$source = '1adapt'; err.adapt$criteria = 'Pareto Distance'
err.rando = SVM.err(fine.input = fine.grid, mod.list = mod.rando)
err.rando$source = '2rando'; err.rando$criteria = 'Pareto Distance'

err.svm.dist = rbind(err.paret, err.adapt, err.rando)

g.rate = ggplot(err.svm.dist) +
  geom_col(mapping = aes(x = source, y = rate, fill = source)) +
  geom_errorbar(mapping = aes(x = source, ymin = rate - err, ymax = rate + err), width = 0.5) +
  facet_wrap(~method, nrow = 1) +
  scale_fill_manual(labels = c('0paret' = 'Starting Dataset', '1adapt' = '+ Adaptive Sampling', 
                                 '2rando' = '+ Random Sampling'), name = '',
                      values = c('0paret' = 'skyblue2', '1adapt' = 'red', '2rando' = 'green')) +
  scale_y_continuous(expand = expansion(mult = c(0, 0.05))) + scale_x_discrete(breaks = '') +
  theme_bw() + labs(x = '', y = 'Total\nError Rate', subtitle = 'Pareto Distance')

g.typ1 = ggplot(err.svm.dist) +
  geom_col(mapping = aes(x = source, y = typ1, fill = source)) +
  geom_errorbar(mapping = aes(x = source, ymin = typ1 - typ1.err, ymax = typ1 + typ1.err), width = 0.5) +
  facet_wrap(~method, nrow = 1) +
  scale_fill_manual(labels = c('0paret' = 'Starting Dataset', '1adapt' = '+ Adaptive Sampling', 
                                 '2rando' = '+ Random Sampling'), name = '',
                      values = c('0paret' = 'skyblue2', '1adapt' = 'red', '2rando' = 'green')) +
  scale_y_continuous(expand = expansion(mult = c(0, 0.05))) + scale_x_discrete(breaks = '') +
  theme_bw() + labs(x = '', y = 'False Positive\nError Rate')

g.typ2 = ggplot(err.svm.dist) +
  geom_col(mapping = aes(x = source, y = typ2, fill = source)) +
  geom_errorbar(mapping = aes(x = source, ymin = typ2 - typ2.err, ymax = typ2 + typ2.err), width = 0.5) +
  facet_wrap(~method, nrow = 1) +
  scale_fill_manual(labels = c('0paret' = 'Starting Dataset', '1adapt' = '+ Adaptive Sampling', 
                                 '2rando' = '+ Random Sampling'), name = '',
                      values = c('0paret' = 'skyblue2', '1adapt' = 'red', '2rando' = 'green')) +
  scale_y_continuous(expand = expansion(mult = c(0, 0.05))) + scale_x_discrete(breaks = '') +
  theme_bw() + labs(x = '', y = 'False Negative\nError Rate')

(g.rate + guides(fill = FALSE)) / g.typ1 / (g.typ2 + guides(fill = FALSE))

write.csv(err.svm.dist, 'SVM_error_delta.csv')
rm(err.paret, err.adapt, err.rando)

Graphically, the selection of the kernel form has a large impact on the quality of the result. The polynomial and radial kernels appear to roughly match the shape of the normalized distance = 1 criterion; the linear and sigmoidal kernels do not appear to match anything. Further tuning of the polynomial degree or coefficient could improve the results, but without a training-testing set delineation, there is no way to validate the results.

svm.margin.paret = marginal(mod = mod.paret); svm.margin.paret$cat = 'start'
svm.margin.adapt = marginal(mod = mod.adapt); svm.margin.adapt$cat = 'adapt'
svm.margin.rando = marginal(mod = mod.rando); svm.margin.rando$cat = 'rando'
svm.margin = rbind(svm.margin.paret, svm.margin.adapt, svm.margin.rando)
rm(svm.margin.paret, svm.margin.adapt, svm.margin.rando)

write.csv(svm.margin, 'Margin_SVM_dist.csv')

Infer.plt = read.csv('../Ex_Quartic/Marginals_delta.csv')
Infer.plt = Infer.plt[,!names(Infer.plt) %in% c('X')]
# names(Infer.plt)
Infer.plt$method = 'GP'

ggplot() +
  geom_path(data = filter(Infer.plt, cat == 'tru'), mapping = aes(x = x, y = prob - psd, color = cat), linetype = 3) +
  geom_path(data = filter(Infer.plt, cat == 'tru'), mapping = aes(x = x, y = prob + psd, color = cat), linetype = 3) +
  geom_path(data = svm.margin, mapping = aes(x = x, y = prob, linetype = method, color = cat)) +
  facet_wrap(~var, nrow = 2) + theme_bw() +
  labs(x = 'Input Value', y = 'Conditional Probability', linetype = 'SVM Kernel', color = '') +
  scale_color_manual(labels = c('tru' = 'Expected Marginal', 'start' = 'Starting Dataset', 
                                  'adapt' = '+ Adaptive Sampling', 'rando' = '+ Random Sampling'),
                     values = c('tru' = 'black', 'start' = 'skyblue2', 
                                  'adapt' = 'red', 'rando' = 'green'),
                     breaks = c('tru', 'start', 'adapt', 'rando')) +
  guides(color = guide_legend(override.aes = list(linetype = c(3, 1, 1, 1)))) +
  scale_linetype_discrete(labels = c('SVM-pol' = 'Polynomial', 'rad' = 'SVM-Radial')) +
  scale_x_continuous(expand = c(0, 0)) + scale_y_continuous(limits = c(0, 1))


ggplot() +
  geom_path(data = filter(Infer.plt, cat == 'tru'), mapping = aes(x = x, y = prob - psd, color = cat), linetype = 3) +
  geom_path(data = filter(Infer.plt, cat == 'tru'), mapping = aes(x = x, y = prob + psd, color = cat), linetype = 3) +
  geom_path(data = filter(Infer.plt, cat != 'tru'), mapping = aes(x = x, y = prob, color = cat)) +
  facet_wrap(var~., nrow = 2) + 
  scale_color_manual(labels = c('tru' = 'Expected Marginal', 'start' = 'Starting Dataset', 
                                  'adapt' = '+ Adaptive Sampling', 'rando' = '+ Random Sampling'),
                     values = c('tru' = 'black', 'start' = 'skyblue2', 
                                  'adapt' = 'red', 'rando' = 'green'),
                     breaks = c('tru', 'start', 'adapt', 'rando')) +
  guides(color = guide_legend(override.aes = list(linetype = c(3, 1, 1, 1)))) +
  labs(x = '', y = 'Probability of Acceptance', subtitle = expression('Pareto Distance'), color = '') +
  scale_y_continuous(expand = c(0, 0.05)) + scale_x_continuous(expand = c(0, 0)) +
  theme_bw()


# Calculate coefficients of determination: easier comparison
coefdet = data.frame(method = c(rep('SVM-pol', 3), rep('SVM-rad', 3), rep('GP', 3)),
           dataset = c('0start', '1adapt', '2rando'),
 coef = c(cor(x = filter(svm.margin, method == 'SVM-pol', cat == 'start')$prob, 
    y = filter(Infer.plt, cat == 'tru')$prob, method = 'pearson')^2,
  cor(x = filter(svm.margin, method == 'SVM-pol', cat == 'adapt')$prob, 
    y = filter(Infer.plt, cat == 'tru')$prob, method = 'pearson')^2,
  cor(x = filter(svm.margin, method == 'SVM-pol', cat == 'rando')$prob, 
    y = filter(Infer.plt, cat == 'tru')$prob, method = 'pearson')^2,
  cor(x = filter(svm.margin, method == 'SVM-rad', cat == 'start')$prob, 
    y = filter(Infer.plt, cat == 'tru')$prob, method = 'pearson')^2,
  cor(x = filter(svm.margin, method == 'SVM-rad', cat == 'adapt')$prob, 
    y = filter(Infer.plt, cat == 'tru')$prob, method = 'pearson')^2,
  cor(x = filter(svm.margin, method == 'SVM-rad', cat == 'rando')$prob, 
    y = filter(Infer.plt, cat == 'tru')$prob, method = 'pearson')^2,
  cor(x = filter(Infer.plt, cat == 'start')$prob, 
    y = c(filter(Infer.plt, cat == 'tru', var == 'x1')$prob, filter(Infer.plt, cat == 'tru', var == 'x2')$prob), 
    method = 'pearson')^2,
  cor(x = filter(Infer.plt, cat == 'adapt')$prob, 
    y = c(filter(Infer.plt, cat == 'tru', var == 'x1')$prob, filter(Infer.plt, cat == 'tru', var == 'x2')$prob), 
    method = 'pearson')^2,
  cor(x = filter(Infer.plt, cat == 'rando')$prob, 
    y = c(filter(Infer.plt, cat == 'tru', var == 'x1')$prob, filter(Infer.plt, cat == 'tru', var == 'x2')$prob), 
    method = 'pearson')^2))

coefdet
write.csv(coefdet, 'Marginals_CoefDet_delta.csv')

ggplot(coefdet) +
  geom_col(mapping = aes(x = dataset, fill = dataset, y = coef))+
  facet_grid(~method) +
  labs(x = '', y = '1-Variable Marginal Coefficient of Determination', subtitle = 'Pareto Distance') +
  scale_x_discrete(breaks = c()) +
  scale_fill_manual(labels = c('0start' = 'Starting Dataset', '1adapt' = '+ Adaptive Sampling', 
                                 '2rando' = '+ Random Sampling'), name = '',
                    values = c('0start' = 'skyblue2', '1adapt' = 'red', '2rando' = 'green')) +
  scale_y_continuous(expand = expansion(mult = c(0, 0.05)))

Single variable marginals are ok visually, but calculating the coefficient of determination shows that the GP method is more consistent regardless of dataset and it demonstrates the expected improvement with increased sampling near the boundary. The SVM methods overall cannot capture the boundary very well, leading to classification errors.

data.paret = read.csv('../Ex_Quartic/GPar_all_start.csv')
data.cutof = read.csv('../Ex_Quartic/GPar_Accept_Threshold.csv')
data.rando = read.csv(file = 'GPar_Random.csv')

# Define acceptance
data.paret$cat = 0; data.cutof$cat = 0; data.rando$cat = 0; fine.grid$cat = 0
data.paret$cat[data.paret$f1.norm <= 1 & data.paret$f2.norm <= 1] = 1
data.cutof$cat[data.cutof$f1.norm <= 1 & data.cutof$f2.norm <= 1] = 1
data.rando$cat[data.rando$f1.norm <= 1 & data.rando$f2.norm <= 1] = 1
fine.grid$cat[fine.grid$f1.norm <= 1 & fine.grid$f2.norm <= 1] = 1

# Plots
mod.paret = SVM.mod.all(input.data = data.paret)
SVM.input(fine.input = fine.grid[,c('x1', 'x2', 'cat')], 
          mod.rad = mod.paret$rad, mod.lin = mod.paret$lin, 
          mod.pol = mod.paret$pol, mod.sig = mod.paret$sig,
          tit = 'Threshold Cutoff, Starting Dataset')


mod.adapt = SVM.mod.all(input.data = data.cutof)
mod.adapt.cutof = mod.adapt
SVM.input(fine.input = fine.grid[,c('x1', 'x2', 'cat')], 
          mod.rad = mod.adapt$rad, mod.lin = mod.adapt$lin, 
          mod.pol = mod.adapt$pol, mod.sig = mod.adapt$sig,
          tit = 'Threshold Cutoff, + Adaptive Sampling')


mod.rando = SVM.mod.all(input.data = data.rando)
SVM.input(fine.input = fine.grid[,c('x1', 'x2', 'cat')], 
          mod.rad = mod.rando$rad, mod.lin = mod.rando$lin, 
          mod.pol = mod.rando$pol, mod.sig = mod.rando$sig,
          tit = 'Threshold Cutoff, + Random Sampling')


# Error rates
err.paret = SVM.err(fine.input = fine.grid, mod.list = mod.paret)
err.paret$source = '0paret'; err.paret$criteria = 'Treshold Cutoff'
err.adapt = SVM.err(fine.input = fine.grid, mod.list = mod.adapt)
err.adapt$source = '1adapt'; err.adapt$criteria = 'Treshold Cutoff'
err.rando = SVM.err(fine.input = fine.grid, mod.list = mod.rando)
err.rando$source = '2rando'; err.rando$criteria = 'Treshold Cutoff'

err.svm.cutof = rbind(err.paret, err.adapt, err.rando)

g.rate = ggplot(err.svm.cutof) +
  geom_col(mapping = aes(x = source, y = rate, fill = source)) +
  geom_errorbar(mapping = aes(x = source, ymin = rate - err, ymax = rate + err), width = 0.5) +
  facet_wrap(~method, nrow = 1) +
  scale_fill_manual(labels = c('0paret' = 'Starting Dataset', '1adapt' = '+ Adaptive Sampling', 
                                 '2rando' = '+ Random Sampling'), name = '',
                      values = c('0paret' = 'skyblue2', '1adapt' = 'red', '2rando' = 'green')) +
  scale_y_continuous(expand = expansion(mult = c(0, 0.05))) + scale_x_discrete(breaks = '') +
  theme_bw() + labs(x = '', y = 'Total\nError Rate', subtitle = 'Cutoff Threshold')

g.typ1 = ggplot(err.svm.cutof) +
  geom_col(mapping = aes(x = source, y = typ1, fill = source)) +
  geom_errorbar(mapping = aes(x = source, ymin = typ1 - typ1.err, ymax = typ1 + typ1.err), width = 0.5) +
  facet_wrap(~method, nrow = 1) +
  scale_fill_manual(labels = c('0paret' = 'Starting Dataset', '1adapt' = '+ Adaptive Sampling', 
                                 '2rando' = '+ Random Sampling'), name = '',
                      values = c('0paret' = 'skyblue2', '1adapt' = 'red', '2rando' = 'green')) +
  scale_y_continuous(expand = expansion(mult = c(0, 0.05))) + scale_x_discrete(breaks = '') +
  theme_bw() + labs(x = '', y = 'False Positive\nError Rate')

g.typ2 = ggplot(err.svm.cutof) +
  geom_col(mapping = aes(x = source, y = typ2, fill = source)) +
  geom_errorbar(mapping = aes(x = source, ymin = typ2 - typ2.err, ymax = typ2 + typ2.err), width = 0.5) +
  facet_wrap(~method, nrow = 1) +
  scale_fill_manual(labels = c('0paret' = 'Starting Dataset', '1adapt' = '+ Adaptive Sampling', 
                                 '2rando' = '+ Random Sampling'), name = '',
                      values = c('0paret' = 'skyblue2', '1adapt' = 'red', '2rando' = 'green')) +
  scale_y_continuous(expand = expansion(mult = c(0, 0.05))) + scale_x_discrete(breaks = '') +
  theme_bw() + labs(x = '', y = 'False Negative\nError Rate')

(g.rate + guides(fill = FALSE)) / g.typ1 / (g.typ2 + guides(fill = FALSE))

write.csv(err.svm.cutof, 'SVM_error_cutof.csv')

rm(err.paret, err.adapt, err.rando, g.rate, g.typ1, g.typ2)
svm.margin.paret = marginal(mod = mod.paret); svm.margin.paret$cat = 'start'
svm.margin.adapt = marginal(mod = mod.adapt); svm.margin.adapt$cat = 'adapt'
svm.margin.rando = marginal(mod = mod.rando); svm.margin.rando$cat = 'rando'
svm.margin = rbind(svm.margin.paret, svm.margin.adapt, svm.margin.rando)
rm(svm.margin.paret, svm.margin.adapt, svm.margin.rando)

write.csv(svm.margin, 'Margin_SVM_cutof.csv')

Infer.plt = read.csv('../Ex_Quartic/Marginals_cutof.csv')
Infer.plt = Infer.plt[,!names(Infer.plt) %in% c('X')]
# names(Infer.plt)
Infer.plt$method = 'GP'

ggplot() +
  geom_path(data = filter(Infer.plt, cat == 'tru'), mapping = aes(x = x, y = prob - psd, color = cat), linetype = 3) +
  geom_path(data = filter(Infer.plt, cat == 'tru'), mapping = aes(x = x, y = prob + psd, color = cat), linetype = 3) +
  geom_path(data = svm.margin, mapping = aes(x = x, y = prob, linetype = method, color = cat)) +
  facet_wrap(~var, nrow = 2) + theme_bw() +
  labs(x = 'Input Value', y = 'Conditional Probability', linetype = 'SVM Kernel', color = '', subtitle = 'Cutoff Threshold') +
  scale_color_manual(labels = c('tru' = 'Expected Marginal', 'start' = 'Starting Dataset', 
                                  'adapt' = '+ Adaptive Sampling', 'rando' = '+ Random Sampling'),
                     values = c('tru' = 'black', 'start' = 'skyblue2', 
                                  'adapt' = 'red', 'rando' = 'green'),
                     breaks = c('tru', 'start', 'adapt', 'rando')) +
  guides(color = guide_legend(override.aes = list(linetype = c(3, 1, 1, 1)))) +
  scale_linetype_discrete(labels = c('SVM-pol' = 'Polynomial', 'rad' = 'SVM-Radial')) +
  scale_x_continuous(expand = c(0, 0)) + scale_y_continuous(limits = c(0, 1))


ggplot() +
  geom_path(data = filter(Infer.plt, cat == 'tru'), mapping = aes(x = x, y = prob - psd, color = cat), linetype = 3) +
  geom_path(data = filter(Infer.plt, cat == 'tru'), mapping = aes(x = x, y = prob + psd, color = cat), linetype = 3) +
  geom_path(data = filter(Infer.plt, cat != 'tru'), mapping = aes(x = x, y = prob, color = cat)) +
  facet_wrap(var~., nrow = 2) + 
  scale_color_manual(labels = c('tru' = 'Expected Marginal', 'start' = 'Starting Dataset', 
                                  'adapt' = '+ Adaptive Sampling', 'rando' = '+ Random Sampling'),
                     values = c('tru' = 'black', 'start' = 'skyblue2', 
                                  'adapt' = 'red', 'rando' = 'green'),
                     breaks = c('tru', 'start', 'adapt', 'rando')) +
  guides(color = guide_legend(override.aes = list(linetype = c(3, 1, 1, 1)))) +
  labs(x = '', y = 'Probability of Acceptance', subtitle = 'Cutoff Threshold', color = '') +
  scale_y_continuous(expand = c(0, 0.05)) + scale_x_continuous(expand = c(0, 0)) +
  theme_bw()


# Calculate coefficients of determination: easier comparison
coefdet = data.frame(method = c(rep('SVM-pol', 3), rep('SVM-rad', 3), rep('GP', 3)),
           dataset = c('0start', '1adapt', '2rando'),
 coef = c(cor(x = filter(svm.margin, method == 'SVM-pol', cat == 'start')$prob, 
    y = filter(Infer.plt, cat == 'tru')$prob, method = 'pearson')^2,
  cor(x = filter(svm.margin, method == 'SVM-pol', cat == 'adapt')$prob, 
    y = filter(Infer.plt, cat == 'tru')$prob, method = 'pearson')^2,
  cor(x = filter(svm.margin, method == 'SVM-pol', cat == 'rando')$prob, 
    y = filter(Infer.plt, cat == 'tru')$prob, method = 'pearson')^2,
  cor(x = filter(svm.margin, method == 'SVM-rad', cat == 'start')$prob, 
    y = filter(Infer.plt, cat == 'tru')$prob, method = 'pearson')^2,
  cor(x = filter(svm.margin, method == 'SVM-rad', cat == 'adapt')$prob, 
    y = filter(Infer.plt, cat == 'tru')$prob, method = 'pearson')^2,
  cor(x = filter(svm.margin, method == 'SVM-rad', cat == 'rando')$prob, 
    y = filter(Infer.plt, cat == 'tru')$prob, method = 'pearson')^2,
  cor(x = filter(Infer.plt, cat == 'start')$prob, 
    y = c(filter(Infer.plt, cat == 'tru', var == 'x1')$prob, filter(Infer.plt, cat == 'tru', var == 'x2')$prob), 
    method = 'pearson')^2,
  cor(x = filter(Infer.plt, cat == 'adapt')$prob, 
    y = c(filter(Infer.plt, cat == 'tru', var == 'x1')$prob, filter(Infer.plt, cat == 'tru', var == 'x2')$prob), 
    method = 'pearson')^2,
  cor(x = filter(Infer.plt, cat == 'rando')$prob, 
    y = c(filter(Infer.plt, cat == 'tru', var == 'x1')$prob, filter(Infer.plt, cat == 'tru', var == 'x2')$prob), 
    method = 'pearson')^2))

coefdet
write.csv(coefdet, 'Marginals_CoefDet_cutof.csv')

ggplot(coefdet) +
  geom_col(mapping = aes(x = dataset, fill = dataset, y = coef))+
  facet_grid(~method) +
  labs(x = '', y = '1-Variable Marginal Coefficient of Determination', subtitle = 'Cutoff Threshold') +
  scale_x_discrete(breaks = c()) +
  scale_fill_manual(labels = c('0start' = 'Starting Dataset', '1adapt' = '+ Adaptive Sampling', 
                                 '2rando' = '+ Random Sampling'), name = '',
                    values = c('0start' = 'skyblue2', '1adapt' = 'red', '2rando' = 'green')) +
  scale_y_continuous(expand = expansion(mult = c(0, 0.05)))

Repeat for radius-angle cutoff

data.paret = read.csv(file = '../Ex_Quartic/GPar_all_start.csv')
data.radan = read.csv('../Ex_Quartic/GPar_Accept_Radius.csv')
data.rando = read.csv(file = 'GPar_Random.csv')

# Define acceptance
data.paret$cat = 0; data.radan$cat = 0; data.rando$cat = 0; fine.grid$cat = 0
data.paret$cat[sqrt(data.paret$f1.norm^2 + data.paret$f2.norm^2) <= 1 & data.paret$theta > 20] = 1
data.radan$cat[sqrt(data.radan$f1.norm^2 + data.radan$f2.norm^2) <= 1 & data.radan$theta > 20] = 1
data.rando$cat[sqrt(data.rando$f1.norm^2 + data.rando$f2.norm^2) <= 1 & data.rando$theta > 20] = 1
fine.grid$cat[sqrt(fine.grid$f1.norm^2 + fine.grid$f2.norm^2) <= 1 & fine.grid$ang > 20] = 1

# Plots
mod.paret = SVM.mod.all(input.data = data.paret)
SVM.input(fine.input = fine.grid[,c('x1', 'x2', 'cat')], 
          mod.rad = mod.paret$rad, mod.lin = mod.paret$lin, 
          mod.pol = mod.paret$pol, mod.sig = mod.paret$sig,
          tit = 'Utopia Distance + Priority, Starting Dataset')


mod.adapt = SVM.mod.all(input.data = data.radan)
mod.adapt.radan = mod.adapt
SVM.input(fine.input = fine.grid[,c('x1', 'x2', 'cat')], 
          mod.rad = mod.adapt$rad, mod.lin = mod.adapt$lin, 
          mod.pol = mod.adapt$pol, mod.sig = mod.adapt$sig,
          tit = 'Utopia Distance + Priority, + Adaptive Sampling')


mod.rando = SVM.mod.all(input.data = data.rando)
SVM.input(fine.input = fine.grid[,c('x1', 'x2', 'cat')], 
          mod.rad = mod.rando$rad, mod.lin = mod.rando$lin, 
          mod.pol = mod.rando$pol, mod.sig = mod.rando$sig,
          tit = 'Utopia Distance + Priority, + Random Sampling')


# Error rates
err.paret = SVM.err(fine.input = fine.grid, mod.list = mod.paret)
err.paret$source = '0paret'; err.paret$criteria = 'Treshold Cutoff'
err.adapt = SVM.err(fine.input = fine.grid, mod.list = mod.adapt)
err.adapt$source = '1adapt'; err.adapt$criteria = 'Treshold Cutoff'
err.rando = SVM.err(fine.input = fine.grid, mod.list = mod.rando)
err.rando$source = '2rando'; err.rando$criteria = 'Treshold Cutoff'

err.svm.radan = rbind(err.paret, err.adapt, err.rando)

g.rate = ggplot(err.svm.radan) +
  geom_col(mapping = aes(x = source, y = rate, fill = source)) +
  geom_errorbar(mapping = aes(x = source, ymin = rate - err, ymax = rate + err), width = 0.5) +
  facet_wrap(~method, nrow = 1) +
  scale_fill_manual(labels = c('0paret' = 'Starting Dataset', '1adapt' = '+ Adaptive Sampling', 
                                 '2rando' = '+ Random Sampling'), name = '',
                      values = c('0paret' = 'skyblue2', '1adapt' = 'red', '2rando' = 'green')) +
  scale_y_continuous(expand = expansion(mult = c(0, 0.05))) + scale_x_discrete(breaks = '') +
  theme_bw() + labs(x = '', y = 'Total\nError Rate', subtitle = 'Utopia Distance + Priority')

g.typ1 = ggplot(err.svm.radan) +
  geom_col(mapping = aes(x = source, y = typ1, fill = source)) +
  geom_errorbar(mapping = aes(x = source, ymin = typ1 - typ1.err, ymax = typ1 + typ1.err), width = 0.5) +
  facet_wrap(~method, nrow = 1) +
  scale_fill_manual(labels = c('0paret' = 'Starting Dataset', '1adapt' = '+ Adaptive Sampling', 
                                 '2rando' = '+ Random Sampling'), name = '',
                      values = c('0paret' = 'skyblue2', '1adapt' = 'red', '2rando' = 'green')) +
  scale_y_continuous(expand = expansion(mult = c(0, 0.05))) + scale_x_discrete(breaks = '') +
  theme_bw() + labs(x = '', y = 'False Positive\nError Rate')

g.typ2 = ggplot(err.svm.radan) +
  geom_col(mapping = aes(x = source, y = typ2, fill = source)) +
  geom_errorbar(mapping = aes(x = source, ymin = typ2 - typ2.err, ymax = typ2 + typ2.err), width = 0.5) +
  facet_wrap(~method, nrow = 1) +
  scale_fill_manual(labels = c('0paret' = 'Starting Dataset', '1adapt' = '+ Adaptive Sampling', 
                                 '2rando' = '+ Random Sampling'), name = '',
                      values = c('0paret' = 'skyblue2', '1adapt' = 'red', '2rando' = 'green')) +
  scale_y_continuous(expand = expansion(mult = c(0, 0.05))) + scale_x_discrete(breaks = '') +
  theme_bw() + labs(x = '', y = 'False Negative\nError Rate')

(g.rate + guides(fill = FALSE)) / g.typ1 / (g.typ2 + guides(fill = FALSE))

write.csv(err.svm.cutof, 'SVM_error_radan.csv')

rm(err.paret, err.adapt, err.rando, g.rate, g.typ1, g.typ2)
svm.margin.paret = marginal(mod = mod.paret); svm.margin.paret$cat = 'start'
svm.margin.adapt = marginal(mod = mod.adapt); svm.margin.adapt$cat = 'adapt'
svm.margin.rando = marginal(mod = mod.rando); svm.margin.rando$cat = 'rando'
svm.margin = rbind(svm.margin.paret, svm.margin.adapt, svm.margin.rando)
rm(svm.margin.paret, svm.margin.adapt, svm.margin.rando)

write.csv(svm.margin, 'Margin_SVM_radan.csv')

Infer.plt = read.csv('../Ex_Quartic/Marginals_radan.csv')
Infer.plt = Infer.plt[,!names(Infer.plt) %in% c('X')]
# names(Infer.plt)
Infer.plt$method = 'GP'

ggplot() +
  geom_path(data = filter(Infer.plt, cat == 'tru'), mapping = aes(x = x, y = prob - psd, color = cat), linetype = 3) +
  geom_path(data = filter(Infer.plt, cat == 'tru'), mapping = aes(x = x, y = prob + psd, color = cat), linetype = 3) +
  geom_path(data = svm.margin, mapping = aes(x = x, y = prob, linetype = method, color = cat)) +
  facet_wrap(~var, nrow = 2) + theme_bw() +
  labs(x = 'Input Value', y = 'Conditional Probability', linetype = 'SVM Kernel', color = '', 
       subtitle = 'Utopia Distance + Priority') +
  scale_color_manual(labels = c('tru' = 'Expected Marginal', 'start' = 'Starting Dataset', 
                                  'adapt' = '+ Adaptive Sampling', 'rando' = '+ Random Sampling'),
                     values = c('tru' = 'black', 'start' = 'skyblue2', 
                                  'adapt' = 'red', 'rando' = 'green'),
                     breaks = c('tru', 'start', 'adapt', 'rando')) +
  guides(color = guide_legend(override.aes = list(linetype = c(3, 1, 1, 1)))) +
  scale_linetype_discrete(labels = c('SVM-pol' = 'Polynomial', 'rad' = 'SVM-Radial')) +
  scale_x_continuous(expand = c(0, 0)) + scale_y_continuous(limits = c(0, 1))


ggplot() +
  geom_path(data = filter(Infer.plt, cat == 'tru'), mapping = aes(x = x, y = prob - psd, color = cat), linetype = 3) +
  geom_path(data = filter(Infer.plt, cat == 'tru'), mapping = aes(x = x, y = prob + psd, color = cat), linetype = 3) +
  geom_path(data = filter(Infer.plt, cat != 'tru'), mapping = aes(x = x, y = prob, color = cat)) +
  facet_wrap(var~., nrow = 2) + 
  scale_color_manual(labels = c('tru' = 'Expected Marginal', 'start' = 'Starting Dataset', 
                                  'adapt' = '+ Adaptive Sampling', 'rando' = '+ Random Sampling'),
                     values = c('tru' = 'black', 'start' = 'skyblue2', 
                                  'adapt' = 'red', 'rando' = 'green'),
                     breaks = c('tru', 'start', 'adapt', 'rando')) +
  guides(color = guide_legend(override.aes = list(linetype = c(3, 1, 1, 1)))) +
  labs(x = '', y = 'Probability of Acceptance', subtitle = 'Cutoff Threshold', color = '') +
  scale_y_continuous(expand = c(0, 0.05)) + scale_x_continuous(expand = c(0, 0)) +
  theme_bw()


# Calculate coefficients of determination: easier comparison
coefdet = data.frame(method = c(rep('SVM-pol', 3), rep('SVM-rad', 3), rep('GP', 3)),
           dataset = c('0start', '1adapt', '2rando'),
 coef = c(cor(x = filter(svm.margin, method == 'SVM-pol', cat == 'start')$prob, 
    y = filter(Infer.plt, cat == 'tru')$prob, method = 'pearson')^2,
  cor(x = filter(svm.margin, method == 'SVM-pol', cat == 'adapt')$prob, 
    y = filter(Infer.plt, cat == 'tru')$prob, method = 'pearson')^2,
  cor(x = filter(svm.margin, method == 'SVM-pol', cat == 'rando')$prob, 
    y = filter(Infer.plt, cat == 'tru')$prob, method = 'pearson')^2,
  cor(x = filter(svm.margin, method == 'SVM-rad', cat == 'start')$prob, 
    y = filter(Infer.plt, cat == 'tru')$prob, method = 'pearson')^2,
  cor(x = filter(svm.margin, method == 'SVM-rad', cat == 'adapt')$prob, 
    y = filter(Infer.plt, cat == 'tru')$prob, method = 'pearson')^2,
  cor(x = filter(svm.margin, method == 'SVM-rad', cat == 'rando')$prob, 
    y = filter(Infer.plt, cat == 'tru')$prob, method = 'pearson')^2,
  cor(x = filter(Infer.plt, cat == 'start')$prob, 
    y = c(filter(Infer.plt, cat == 'tru', var == 'x1')$prob, filter(Infer.plt, cat == 'tru', var == 'x2')$prob), 
    method = 'pearson')^2,
  cor(x = filter(Infer.plt, cat == 'adapt')$prob, 
    y = c(filter(Infer.plt, cat == 'tru', var == 'x1')$prob, filter(Infer.plt, cat == 'tru', var == 'x2')$prob), 
    method = 'pearson')^2,
  cor(x = filter(Infer.plt, cat == 'rando')$prob, 
    y = c(filter(Infer.plt, cat == 'tru', var == 'x1')$prob, filter(Infer.plt, cat == 'tru', var == 'x2')$prob), 
    method = 'pearson')^2))

coefdet
write.csv(coefdet, 'Marginals_CoefDet_radan.csv')

ggplot(coefdet) +
  geom_col(mapping = aes(x = dataset, fill = dataset, y = coef))+
  facet_grid(~method) +
  labs(x = '', y = '1-Variable Marginal Coefficient of Determination', subtitle = 'Utopia Distance + Priority') +
  scale_x_discrete(breaks = c()) +
  scale_fill_manual(labels = c('0start' = 'Starting Dataset', '1adapt' = '+ Adaptive Sampling', 
                                 '2rando' = '+ Random Sampling'), name = '',
                    values = c('0start' = 'skyblue2', '1adapt' = 'red', '2rando' = 'green')) +
  scale_y_continuous(expand = expansion(mult = c(0, 0.05)))

import.marginal = function(svm.margin, typ){
  # Set up output
  import.svm.margin = data.frame()
  # Loop across methods
  for(met in unique(svm.margin$method)){
    sub = filter(svm.margin, cat == 'adapt', var == 'x1', method == met)
    import.svm.margin = rbind(import.svm.margin, data.frame(
      import = diff(range(sub$prob))/sum(sub$psd),
      var = 'x1',
      method = paste('Marginal', substring(met, 4), sep = ''),
      sd = sqrt((max(sub$prob)*(1-max(sub$prob)) + min(sub$prob)*(1-min(sub$prob)) * 
                    diff(range(sub$prob)) + var(sub$psd))/nrow(sub))
      ))
    sub = filter(svm.margin, cat == 'adapt', var == 'x2', method == met)
    import.svm.margin = rbind(import.svm.margin, data.frame(
      import = diff(range(sub$prob))/sum(sub$psd),
      var = 'x2',
      method = paste('Marginal', substring(met, 4), sep = ''),
      sd = sqrt((max(sub$prob)*(1-max(sub$prob)) + min(sub$prob)*(1-min(sub$prob)) * 
                    diff(range(sub$prob)) + var(sub$psd))/nrow(sub))
      ))
  }
  # Type, relative importance
  import.svm.margin$typ = typ
  import.svm.margin$r.import = c(import.svm.margin$import[1:2]/max(import.svm.margin$import[1:2]),
                                 import.svm.margin$import[3:4]/max(import.svm.margin$import[3:4]))
  return(import.svm.margin)
}

svm.import.margin = rbind(import.marginal(svm.margin = read.csv('Margin_SVM_dist.csv'), typ = 'Pareto Distance'), 
      import.marginal(svm.margin = read.csv('Margin_SVM_cutof.csv'), typ = 'Threshold Cutoff'), 
      import.marginal(svm.margin = read.csv('Margin_SVM_radan.csv'), typ = 'Utopia Distance'))
import.rank = read.csv('../Ex_Quartic/Importance.csv')
import.rank = import.rank[, !names(import.rank) %in% c('X')]
import.rank$method = unlist(lapply(import.rank$method, function(x) paste(x, '-GP', sep = '')))
import.rank
# import.rank$method[import.rank$method == 'Shapley'] = 'Shapley-GP'

SVM.shap.delta = SVM.shap(mod = mod.adapt.delta)
SVM.shap.delta$typ = 'Pareto Distance'
SVM.shap.cutof = SVM.shap(mod = mod.adapt.cutof)
SVM.shap.cutof$typ = 'Threshold Cutoff'
SVM.shap.radan = SVM.shap(mod = mod.adapt.radan)
SVM.shap.radan$typ = 'Utopia Distance'


ggplot(rbind(import.rank, SVM.shap.delta, SVM.shap.cutof, SVM.shap.radan, svm.import.margin)) +
  geom_col(mapping = aes(x = var, y = abs(r.import), fill = var)) +
  facet_grid(method~typ, scales = 'free_y') +
  labs(x = '', y = 'Relative Importance') +
  guides(fill = FALSE) + scale_x_discrete(labels = c('x1' = expression('x'[1]), 'x2' = expression('x'[2])))


ggplot(rbind(import.rank, SVM.shap.delta, SVM.shap.cutof, SVM.shap.radan, svm.import.margin)) +
  geom_col(mapping = aes(x = var, y = import, fill = var)) +
  facet_grid(method~typ, scales = 'free_y') +
  labs(x = '', y = 'Relative Importance') +
  guides(fill = FALSE) + scale_x_discrete(labels = c('x1' = expression('x'[1]), 'x2' = expression('x'[2])))


# Rows: Selection method = Pareto distance, Objective Cutoff, Utopia Distance + Priority
# Columns: method = Shapley with SVM, Shapley with GP, new method
# Split up by model method
import.all = rbind(import.rank, SVM.shap.delta, SVM.shap.cutof, SVM.shap.radan, svm.import.margin)
# Classification split
split = unlist(lapply(import.all$method, strsplit, '-'))
import.all$method = split[c(TRUE, FALSE)]
import.all$model = split[c(FALSE, TRUE)]

import.all
ggplot(import.all) +
  geom_col(mapping = aes(x = model, y = import, fill = var), position = 'dodge') +
  geom_errorbar(mapping = aes(x = model, ymax = import + sd, ymin = import - sd, group = var), 
                position = 'dodge', width = 0.9) +
  facet_grid(method~typ, scales = 'free_y') +
  labs(x = 'Model', y = 'Relative Importance (Unitless)') +
  scale_x_discrete(labels = c('GP', 'pol' = 'SVM-pol', 'rad' = 'SVM-rad')) +
  scale_fill_discrete(name = '', labels = c('x1' = expression('x'[1]), 'x2' = expression('x'[2])))


# Relative importance (normalized): uncertainty is divided by the same value
r.max = import.all$import
for(i in seq(from = 1, to = length(r.max)-1, by = 2)){
  r.max[c(i,i+1)] = max(abs(r.max[c(i,i+1)]))
}
import.all$r.import = abs(import.all$import)/r.max
import.all$r.sd = import.all$sd / r.max
ggplot(import.all) +
  geom_col(mapping = aes(x = model, y = r.import, fill = var), position = 'dodge') +
  geom_errorbar(mapping = aes(x = model, ymax = r.import + r.sd, ymin = r.import - r.sd, group = var), 
                position = 'dodge', width = 0.9) +
  facet_grid(method~typ, scales = 'free_y') +
  labs(x = 'Model', y = 'Relative Importance (Unitless)') +
  scale_x_discrete(labels = c('GP', 'pol' = 'SVM-pol', 'rad' = 'SVM-rad')) +
  scale_fill_discrete(name = '', labels = c('x1' = expression('x'[1]), 'x2' = expression('x'[2])))


rm(r.max)

write.csv(import.all, 'ImportRank_Method.csv')

Existing Method: Gaussian Mixtures

Since the GM models are unsupervised, there cannot be control for what the selection criteria are. A superficial analysis will be conducted to check what the ML algorithm is converging to, but the results will be interpreted solely in terms of descriptive statistics.

For the purposes of analysis, three models will be tested based on the input to the ML algorithm: * (x1, x2, f1, f2): the full set of inputs and outputs - since it has the most data, this will likely be the most robust * (x1, x2): negative control of just the inputs - it should group the points based on the sampling densities * (f1, f2): outputs only - this should have the best distinction between good and bad performing groups

Up to 12 categories will be allowed, and any type of Gaussian models will be accepted. This will lead to longer fitting time, but should help with accuracy.

GPar.all = read.csv(file = '../Ex_Quartic/GPar_all_start.csv')

# names(GPar.all)
max.cat = 12
cluster.all = Mclust(data = GPar.all[,c('x1', 'x2', 'f1', 'f2')], G = 2:max.cat)
fitting ...

  |                                                                                                                   
  |                                                                                                             |   0%
  |                                                                                                                   
  |=                                                                                                            |   1%
  |                                                                                                                   
  |==                                                                                                           |   2%
  |                                                                                                                   
  |===                                                                                                          |   3%
  |                                                                                                                   
  |====                                                                                                         |   3%
  |                                                                                                                   
  |====                                                                                                         |   4%
  |                                                                                                                   
  |=====                                                                                                        |   5%
  |                                                                                                                   
  |======                                                                                                       |   5%
  |                                                                                                                   
  |======                                                                                                       |   6%
  |                                                                                                                   
  |=======                                                                                                      |   6%
  |                                                                                                                   
  |========                                                                                                     |   7%
  |                                                                                                                   
  |========                                                                                                     |   8%
  |                                                                                                                   
  |=========                                                                                                    |   8%
  |                                                                                                                   
  |==========                                                                                                   |   9%
  |                                                                                                                   
  |===========                                                                                                  |  10%
  |                                                                                                                   
  |============                                                                                                 |  11%
  |                                                                                                                   
  |=============                                                                                                |  12%
  |                                                                                                                   
  |==============                                                                                               |  13%
  |                                                                                                                   
  |===============                                                                                              |  14%
  |                                                                                                                   
  |================                                                                                             |  15%
  |                                                                                                                   
  |=================                                                                                            |  15%
  |                                                                                                                   
  |==================                                                                                           |  16%
  |                                                                                                                   
  |==================                                                                                           |  17%
  |                                                                                                                   
  |===================                                                                                          |  17%
  |                                                                                                                   
  |====================                                                                                         |  18%
  |                                                                                                                   
  |====================                                                                                         |  19%
  |                                                                                                                   
  |=====================                                                                                        |  19%
  |                                                                                                                   
  |======================                                                                                       |  20%
  |                                                                                                                   
  |=======================                                                                                      |  21%
  |                                                                                                                   
  |========================                                                                                     |  22%
  |                                                                                                                   
  |=========================                                                                                    |  23%
  |                                                                                                                   
  |==========================                                                                                   |  24%
  |                                                                                                                   
  |===========================                                                                                  |  25%
  |                                                                                                                   
  |============================                                                                                 |  26%
  |                                                                                                                   
  |=============================                                                                                |  26%
  |                                                                                                                   
  |==============================                                                                               |  27%
  |                                                                                                                   
  |==============================                                                                               |  28%
  |                                                                                                                   
  |===============================                                                                              |  28%
  |                                                                                                                   
  |================================                                                                             |  29%
  |                                                                                                                   
  |================================                                                                             |  30%
  |                                                                                                                   
  |=================================                                                                            |  30%
  |                                                                                                                   
  |==================================                                                                           |  31%
  |                                                                                                                   
  |==================================                                                                           |  32%
  |                                                                                                                   
  |===================================                                                                          |  32%
  |                                                                                                                   
  |====================================                                                                         |  33%
  |                                                                                                                   
  |=====================================                                                                        |  34%
  |                                                                                                                   
  |======================================                                                                       |  35%
  |                                                                                                                   
  |=======================================                                                                      |  35%
  |                                                                                                                   
  |=======================================                                                                      |  36%
  |                                                                                                                   
  |========================================                                                                     |  37%
  |                                                                                                                   
  |=========================================                                                                    |  37%
  |                                                                                                                   
  |=========================================                                                                    |  38%
  |                                                                                                                   
  |==========================================                                                                   |  39%
  |                                                                                                                   
  |===========================================                                                                  |  39%
  |                                                                                                                   
  |============================================                                                                 |  40%
  |                                                                                                                   
  |============================================                                                                 |  41%
  |                                                                                                                   
  |=============================================                                                                |  41%
  |                                                                                                                   
  |==============================================                                                               |  42%
  |                                                                                                                   
  |==============================================                                                               |  43%
  |                                                                                                                   
  |===============================================                                                              |  43%
  |                                                                                                                   
  |================================================                                                             |  44%
  |                                                                                                                   
  |=================================================                                                            |  45%
  |                                                                                                                   
  |==================================================                                                           |  46%
  |                                                                                                                   
  |===================================================                                                          |  46%
  |                                                                                                                   
  |===================================================                                                          |  47%
  |                                                                                                                   
  |====================================================                                                         |  48%
  |                                                                                                                   
  |=====================================================                                                        |  48%
  |                                                                                                                   
  |=====================================================                                                        |  49%
  |                                                                                                                   
  |======================================================                                                       |  50%
  |                                                                                                                   
  |=======================================================                                                      |  50%
  |                                                                                                                   
  |========================================================                                                     |  51%
  |                                                                                                                   
  |========================================================                                                     |  52%
  |                                                                                                                   
  |=========================================================                                                    |  52%
  |                                                                                                                   
  |==========================================================                                                   |  53%
  |                                                                                                                   
  |==========================================================                                                   |  54%
  |                                                                                                                   
  |===========================================================                                                  |  54%
  |                                                                                                                   
  |============================================================                                                 |  55%
  |                                                                                                                   
  |=============================================================                                                |  56%
  |                                                                                                                   
  |==============================================================                                               |  57%
  |                                                                                                                   
  |===============================================================                                              |  57%
  |                                                                                                                   
  |===============================================================                                              |  58%
  |                                                                                                                   
  |================================================================                                             |  59%
  |                                                                                                                   
  |=================================================================                                            |  59%
  |                                                                                                                   
  |=================================================================                                            |  60%
  |                                                                                                                   
  |==================================================================                                           |  61%
  |                                                                                                                   
  |===================================================================                                          |  61%
  |                                                                                                                   
  |====================================================================                                         |  62%
  |                                                                                                                   
  |====================================================================                                         |  63%
  |                                                                                                                   
  |=====================================================================                                        |  63%
  |                                                                                                                   
  |======================================================================                                       |  64%
  |                                                                                                                   
  |======================================================================                                       |  65%
  |                                                                                                                   
  |=======================================================================                                      |  65%
  |                                                                                                                   
  |========================================================================                                     |  66%
  |                                                                                                                   
  |=========================================================================                                    |  67%
  |                                                                                                                   
  |==========================================================================                                   |  68%
  |                                                                                                                   
  |===========================================================================                                  |  68%
  |                                                                                                                   
  |===========================================================================                                  |  69%
  |                                                                                                                   
  |============================================================================                                 |  70%
  |                                                                                                                   
  |=============================================================================                                |  70%
  |                                                                                                                   
  |=============================================================================                                |  71%
  |                                                                                                                   
  |==============================================================================                               |  72%
  |                                                                                                                   
  |===============================================================================                              |  72%
  |                                                                                                                   
  |===============================================================================                              |  73%
  |                                                                                                                   
  |================================================================================                             |  74%
  |                                                                                                                   
  |=================================================================================                            |  74%
  |                                                                                                                   
  |==================================================================================                           |  75%
  |                                                                                                                   
  |===================================================================================                          |  76%
  |                                                                                                                   
  |====================================================================================                         |  77%
  |                                                                                                                   
  |=====================================================================================                        |  78%
  |                                                                                                                   
  |======================================================================================                       |  79%
  |                                                                                                                   
  |=======================================================================================                      |  80%
  |                                                                                                                   
  |========================================================================================                     |  81%
  |                                                                                                                   
  |=========================================================================================                    |  81%
  |                                                                                                                   
  |=========================================================================================                    |  82%
  |                                                                                                                   
  |==========================================================================================                   |  83%
  |                                                                                                                   
  |===========================================================================================                  |  83%
  |                                                                                                                   
  |===========================================================================================                  |  84%
  |                                                                                                                   
  |============================================================================================                 |  85%
  |                                                                                                                   
  |=============================================================================================                |  85%
  |                                                                                                                   
  |==============================================================================================               |  86%
  |                                                                                                                   
  |===============================================================================================              |  87%
  |                                                                                                                   
  |================================================================================================             |  88%
  |                                                                                                                   
  |=================================================================================================            |  89%
  |                                                                                                                   
  |==================================================================================================           |  90%
  |                                                                                                                   
  |===================================================================================================          |  91%
  |                                                                                                                   
  |====================================================================================================         |  92%
  |                                                                                                                   
  |=====================================================================================================        |  92%
  |                                                                                                                   
  |=====================================================================================================        |  93%
  |                                                                                                                   
  |======================================================================================================       |  94%
  |                                                                                                                   
  |=======================================================================================================      |  94%
  |                                                                                                                   
  |=======================================================================================================      |  95%
  |                                                                                                                   
  |========================================================================================================     |  95%
  |                                                                                                                   
  |=========================================================================================================    |  96%
  |                                                                                                                   
  |=========================================================================================================    |  97%
  |                                                                                                                   
  |==========================================================================================================   |  97%
  |                                                                                                                   
  |===========================================================================================================  |  98%
  |                                                                                                                   
  |============================================================================================================ |  99%
  |                                                                                                                   
  |=============================================================================================================| 100%
cluster.in = Mclust(data = GPar.all[,c('x1', 'x2')], G = 2:max.cat)
fitting ...

  |                                                                                                                   
  |                                                                                                             |   0%
  |                                                                                                                   
  |=                                                                                                            |   1%
  |                                                                                                                   
  |==                                                                                                           |   2%
  |                                                                                                                   
  |===                                                                                                          |   3%
  |                                                                                                                   
  |====                                                                                                         |   3%
  |                                                                                                                   
  |====                                                                                                         |   4%
  |                                                                                                                   
  |=====                                                                                                        |   5%
  |                                                                                                                   
  |======                                                                                                       |   5%
  |                                                                                                                   
  |======                                                                                                       |   6%
  |                                                                                                                   
  |=======                                                                                                      |   6%
  |                                                                                                                   
  |========                                                                                                     |   7%
  |                                                                                                                   
  |========                                                                                                     |   8%
  |                                                                                                                   
  |=========                                                                                                    |   8%
  |                                                                                                                   
  |==========                                                                                                   |   9%
  |                                                                                                                   
  |===========                                                                                                  |  10%
  |                                                                                                                   
  |============                                                                                                 |  11%
  |                                                                                                                   
  |=============                                                                                                |  12%
  |                                                                                                                   
  |==============                                                                                               |  13%
  |                                                                                                                   
  |===============                                                                                              |  14%
  |                                                                                                                   
  |================                                                                                             |  15%
  |                                                                                                                   
  |=================                                                                                            |  15%
  |                                                                                                                   
  |==================                                                                                           |  16%
  |                                                                                                                   
  |==================                                                                                           |  17%
  |                                                                                                                   
  |===================                                                                                          |  17%
  |                                                                                                                   
  |====================                                                                                         |  18%
  |                                                                                                                   
  |====================                                                                                         |  19%
  |                                                                                                                   
  |=====================                                                                                        |  19%
  |                                                                                                                   
  |======================                                                                                       |  20%
  |                                                                                                                   
  |=======================                                                                                      |  21%
  |                                                                                                                   
  |========================                                                                                     |  22%
  |                                                                                                                   
  |=========================                                                                                    |  23%
  |                                                                                                                   
  |==========================                                                                                   |  24%
  |                                                                                                                   
  |===========================                                                                                  |  25%
  |                                                                                                                   
  |============================                                                                                 |  26%
  |                                                                                                                   
  |=============================                                                                                |  26%
  |                                                                                                                   
  |==============================                                                                               |  27%
  |                                                                                                                   
  |==============================                                                                               |  28%
  |                                                                                                                   
  |===============================                                                                              |  28%
  |                                                                                                                   
  |================================                                                                             |  29%
  |                                                                                                                   
  |================================                                                                             |  30%
  |                                                                                                                   
  |=================================                                                                            |  30%
  |                                                                                                                   
  |==================================                                                                           |  31%
  |                                                                                                                   
  |==================================                                                                           |  32%
  |                                                                                                                   
  |===================================                                                                          |  32%
  |                                                                                                                   
  |====================================                                                                         |  33%
  |                                                                                                                   
  |=====================================                                                                        |  34%
  |                                                                                                                   
  |======================================                                                                       |  35%
  |                                                                                                                   
  |=======================================                                                                      |  35%
  |                                                                                                                   
  |=======================================                                                                      |  36%
  |                                                                                                                   
  |========================================                                                                     |  37%
  |                                                                                                                   
  |=========================================                                                                    |  37%
  |                                                                                                                   
  |=========================================                                                                    |  38%
  |                                                                                                                   
  |==========================================                                                                   |  39%
  |                                                                                                                   
  |===========================================                                                                  |  39%
  |                                                                                                                   
  |============================================                                                                 |  40%
  |                                                                                                                   
  |============================================                                                                 |  41%
  |                                                                                                                   
  |=============================================                                                                |  41%
  |                                                                                                                   
  |==============================================                                                               |  42%
  |                                                                                                                   
  |==============================================                                                               |  43%
  |                                                                                                                   
  |===============================================                                                              |  43%
  |                                                                                                                   
  |================================================                                                             |  44%
  |                                                                                                                   
  |=================================================                                                            |  45%
  |                                                                                                                   
  |==================================================                                                           |  46%
  |                                                                                                                   
  |===================================================                                                          |  46%
  |                                                                                                                   
  |===================================================                                                          |  47%
  |                                                                                                                   
  |====================================================                                                         |  48%
  |                                                                                                                   
  |=====================================================                                                        |  48%
  |                                                                                                                   
  |=====================================================                                                        |  49%
  |                                                                                                                   
  |======================================================                                                       |  50%
  |                                                                                                                   
  |=======================================================                                                      |  50%
  |                                                                                                                   
  |========================================================                                                     |  51%
  |                                                                                                                   
  |========================================================                                                     |  52%
  |                                                                                                                   
  |=========================================================                                                    |  52%
  |                                                                                                                   
  |==========================================================                                                   |  53%
  |                                                                                                                   
  |==========================================================                                                   |  54%
  |                                                                                                                   
  |===========================================================                                                  |  54%
  |                                                                                                                   
  |============================================================                                                 |  55%
  |                                                                                                                   
  |=============================================================                                                |  56%
  |                                                                                                                   
  |==============================================================                                               |  57%
  |                                                                                                                   
  |===============================================================                                              |  57%
  |                                                                                                                   
  |===============================================================                                              |  58%
  |                                                                                                                   
  |================================================================                                             |  59%
  |                                                                                                                   
  |=================================================================                                            |  59%
  |                                                                                                                   
  |=================================================================                                            |  60%
  |                                                                                                                   
  |==================================================================                                           |  61%
  |                                                                                                                   
  |===================================================================                                          |  61%
  |                                                                                                                   
  |====================================================================                                         |  62%
  |                                                                                                                   
  |====================================================================                                         |  63%
  |                                                                                                                   
  |=====================================================================                                        |  63%
  |                                                                                                                   
  |======================================================================                                       |  64%
  |                                                                                                                   
  |======================================================================                                       |  65%
  |                                                                                                                   
  |=======================================================================                                      |  65%
  |                                                                                                                   
  |========================================================================                                     |  66%
  |                                                                                                                   
  |=========================================================================                                    |  67%
  |                                                                                                                   
  |==========================================================================                                   |  68%
  |                                                                                                                   
  |===========================================================================                                  |  68%
  |                                                                                                                   
  |===========================================================================                                  |  69%
  |                                                                                                                   
  |============================================================================                                 |  70%
  |                                                                                                                   
  |=============================================================================                                |  70%
  |                                                                                                                   
  |=============================================================================                                |  71%
  |                                                                                                                   
  |==============================================================================                               |  72%
  |                                                                                                                   
  |===============================================================================                              |  72%
  |                                                                                                                   
  |===============================================================================                              |  73%
  |                                                                                                                   
  |================================================================================                             |  74%
  |                                                                                                                   
  |=================================================================================                            |  74%
  |                                                                                                                   
  |==================================================================================                           |  75%
  |                                                                                                                   
  |===================================================================================                          |  76%
  |                                                                                                                   
  |====================================================================================                         |  77%
  |                                                                                                                   
  |=====================================================================================                        |  78%
  |                                                                                                                   
  |======================================================================================                       |  79%
  |                                                                                                                   
  |=======================================================================================                      |  80%
  |                                                                                                                   
  |========================================================================================                     |  81%
  |                                                                                                                   
  |=========================================================================================                    |  81%
  |                                                                                                                   
  |=========================================================================================                    |  82%
  |                                                                                                                   
  |==========================================================================================                   |  83%
  |                                                                                                                   
  |===========================================================================================                  |  83%
  |                                                                                                                   
  |===========================================================================================                  |  84%
  |                                                                                                                   
  |============================================================================================                 |  85%
  |                                                                                                                   
  |=============================================================================================                |  85%
  |                                                                                                                   
  |==============================================================================================               |  86%
  |                                                                                                                   
  |===============================================================================================              |  87%
  |                                                                                                                   
  |================================================================================================             |  88%
  |                                                                                                                   
  |=================================================================================================            |  89%
  |                                                                                                                   
  |==================================================================================================           |  90%
  |                                                                                                                   
  |===================================================================================================          |  91%
  |                                                                                                                   
  |====================================================================================================         |  92%
  |                                                                                                                   
  |=====================================================================================================        |  92%
  |                                                                                                                   
  |=====================================================================================================        |  93%
  |                                                                                                                   
  |======================================================================================================       |  94%
  |                                                                                                                   
  |=======================================================================================================      |  94%
  |                                                                                                                   
  |=======================================================================================================      |  95%
  |                                                                                                                   
  |========================================================================================================     |  95%
  |                                                                                                                   
  |=========================================================================================================    |  96%
  |                                                                                                                   
  |=========================================================================================================    |  97%
  |                                                                                                                   
  |==========================================================================================================   |  97%
  |                                                                                                                   
  |===========================================================================================================  |  98%
  |                                                                                                                   
  |============================================================================================================ |  99%
  |                                                                                                                   
  |=============================================================================================================| 100%
cluster.out = Mclust(data = GPar.all[,c('f1', 'f2')], G = 2:max.cat)
fitting ...

  |                                                                                                                   
  |                                                                                                             |   0%
  |                                                                                                                   
  |=                                                                                                            |   1%
  |                                                                                                                   
  |==                                                                                                           |   2%
  |                                                                                                                   
  |===                                                                                                          |   3%
  |                                                                                                                   
  |====                                                                                                         |   3%
  |                                                                                                                   
  |====                                                                                                         |   4%
  |                                                                                                                   
  |=====                                                                                                        |   5%
  |                                                                                                                   
  |======                                                                                                       |   5%
  |                                                                                                                   
  |======                                                                                                       |   6%
  |                                                                                                                   
  |=======                                                                                                      |   6%
  |                                                                                                                   
  |========                                                                                                     |   7%
  |                                                                                                                   
  |========                                                                                                     |   8%
  |                                                                                                                   
  |=========                                                                                                    |   8%
  |                                                                                                                   
  |==========                                                                                                   |   9%
  |                                                                                                                   
  |===========                                                                                                  |  10%
  |                                                                                                                   
  |============                                                                                                 |  11%
  |                                                                                                                   
  |=============                                                                                                |  12%
  |                                                                                                                   
  |==============                                                                                               |  13%
  |                                                                                                                   
  |===============                                                                                              |  14%
  |                                                                                                                   
  |================                                                                                             |  15%
  |                                                                                                                   
  |=================                                                                                            |  15%
  |                                                                                                                   
  |==================                                                                                           |  16%
  |                                                                                                                   
  |==================                                                                                           |  17%
  |                                                                                                                   
  |===================                                                                                          |  17%
  |                                                                                                                   
  |====================                                                                                         |  18%
  |                                                                                                                   
  |====================                                                                                         |  19%
  |                                                                                                                   
  |=====================                                                                                        |  19%
  |                                                                                                                   
  |======================                                                                                       |  20%
  |                                                                                                                   
  |=======================                                                                                      |  21%
  |                                                                                                                   
  |========================                                                                                     |  22%
  |                                                                                                                   
  |=========================                                                                                    |  23%
  |                                                                                                                   
  |==========================                                                                                   |  24%
  |                                                                                                                   
  |===========================                                                                                  |  25%
  |                                                                                                                   
  |============================                                                                                 |  26%
  |                                                                                                                   
  |=============================                                                                                |  26%
  |                                                                                                                   
  |==============================                                                                               |  27%
  |                                                                                                                   
  |==============================                                                                               |  28%
  |                                                                                                                   
  |===============================                                                                              |  28%
  |                                                                                                                   
  |================================                                                                             |  29%
  |                                                                                                                   
  |================================                                                                             |  30%
  |                                                                                                                   
  |=================================                                                                            |  30%
  |                                                                                                                   
  |==================================                                                                           |  31%
  |                                                                                                                   
  |==================================                                                                           |  32%
  |                                                                                                                   
  |===================================                                                                          |  32%
  |                                                                                                                   
  |====================================                                                                         |  33%
  |                                                                                                                   
  |=====================================                                                                        |  34%
  |                                                                                                                   
  |======================================                                                                       |  35%
  |                                                                                                                   
  |=======================================                                                                      |  35%
  |                                                                                                                   
  |=======================================                                                                      |  36%
  |                                                                                                                   
  |========================================                                                                     |  37%
  |                                                                                                                   
  |=========================================                                                                    |  37%
  |                                                                                                                   
  |=========================================                                                                    |  38%
  |                                                                                                                   
  |==========================================                                                                   |  39%
  |                                                                                                                   
  |===========================================                                                                  |  39%
  |                                                                                                                   
  |============================================                                                                 |  40%
  |                                                                                                                   
  |============================================                                                                 |  41%
  |                                                                                                                   
  |=============================================                                                                |  41%
  |                                                                                                                   
  |==============================================                                                               |  42%
  |                                                                                                                   
  |==============================================                                                               |  43%
  |                                                                                                                   
  |===============================================                                                              |  43%
  |                                                                                                                   
  |================================================                                                             |  44%
  |                                                                                                                   
  |=================================================                                                            |  45%
  |                                                                                                                   
  |==================================================                                                           |  46%
  |                                                                                                                   
  |===================================================                                                          |  46%
  |                                                                                                                   
  |===================================================                                                          |  47%
  |                                                                                                                   
  |====================================================                                                         |  48%
  |                                                                                                                   
  |=====================================================                                                        |  48%
  |                                                                                                                   
  |=====================================================                                                        |  49%
  |                                                                                                                   
  |======================================================                                                       |  50%
  |                                                                                                                   
  |=======================================================                                                      |  50%
  |                                                                                                                   
  |========================================================                                                     |  51%
  |                                                                                                                   
  |========================================================                                                     |  52%
  |                                                                                                                   
  |=========================================================                                                    |  52%
  |                                                                                                                   
  |==========================================================                                                   |  53%
  |                                                                                                                   
  |==========================================================                                                   |  54%
  |                                                                                                                   
  |===========================================================                                                  |  54%
  |                                                                                                                   
  |============================================================                                                 |  55%
  |                                                                                                                   
  |=============================================================                                                |  56%
  |                                                                                                                   
  |==============================================================                                               |  57%
  |                                                                                                                   
  |===============================================================                                              |  57%
  |                                                                                                                   
  |===============================================================                                              |  58%
  |                                                                                                                   
  |================================================================                                             |  59%
  |                                                                                                                   
  |=================================================================                                            |  59%
  |                                                                                                                   
  |=================================================================                                            |  60%
  |                                                                                                                   
  |==================================================================                                           |  61%
  |                                                                                                                   
  |===================================================================                                          |  61%
  |                                                                                                                   
  |====================================================================                                         |  62%
  |                                                                                                                   
  |====================================================================                                         |  63%
  |                                                                                                                   
  |=====================================================================                                        |  63%
  |                                                                                                                   
  |======================================================================                                       |  64%
  |                                                                                                                   
  |======================================================================                                       |  65%
  |                                                                                                                   
  |=======================================================================                                      |  65%
  |                                                                                                                   
  |========================================================================                                     |  66%
  |                                                                                                                   
  |=========================================================================                                    |  67%
  |                                                                                                                   
  |==========================================================================                                   |  68%
  |                                                                                                                   
  |===========================================================================                                  |  68%
  |                                                                                                                   
  |===========================================================================                                  |  69%
  |                                                                                                                   
  |============================================================================                                 |  70%
  |                                                                                                                   
  |=============================================================================                                |  70%
  |                                                                                                                   
  |=============================================================================                                |  71%
  |                                                                                                                   
  |==============================================================================                               |  72%
  |                                                                                                                   
  |===============================================================================                              |  72%
  |                                                                                                                   
  |===============================================================================                              |  73%
  |                                                                                                                   
  |================================================================================                             |  74%
  |                                                                                                                   
  |=================================================================================                            |  74%
  |                                                                                                                   
  |==================================================================================                           |  75%
  |                                                                                                                   
  |===================================================================================                          |  76%
  |                                                                                                                   
  |====================================================================================                         |  77%
  |                                                                                                                   
  |=====================================================================================                        |  78%
  |                                                                                                                   
  |======================================================================================                       |  79%
  |                                                                                                                   
  |=======================================================================================                      |  80%
  |                                                                                                                   
  |========================================================================================                     |  81%
  |                                                                                                                   
  |=========================================================================================                    |  81%
  |                                                                                                                   
  |=========================================================================================                    |  82%
  |                                                                                                                   
  |==========================================================================================                   |  83%
  |                                                                                                                   
  |===========================================================================================                  |  83%
  |                                                                                                                   
  |===========================================================================================                  |  84%
  |                                                                                                                   
  |============================================================================================                 |  85%
  |                                                                                                                   
  |=============================================================================================                |  85%
  |                                                                                                                   
  |==============================================================================================               |  86%
  |                                                                                                                   
  |===============================================================================================              |  87%
  |                                                                                                                   
  |================================================================================================             |  88%
  |                                                                                                                   
  |=================================================================================================            |  89%
  |                                                                                                                   
  |==================================================================================================           |  90%
  |                                                                                                                   
  |===================================================================================================          |  91%
  |                                                                                                                   
  |====================================================================================================         |  92%
  |                                                                                                                   
  |=====================================================================================================        |  92%
  |                                                                                                                   
  |=====================================================================================================        |  93%
  |                                                                                                                   
  |======================================================================================================       |  94%
  |                                                                                                                   
  |=======================================================================================================      |  94%
  |                                                                                                                   
  |=======================================================================================================      |  95%
  |                                                                                                                   
  |========================================================================================================     |  95%
  |                                                                                                                   
  |=========================================================================================================    |  96%
  |                                                                                                                   
  |=========================================================================================================    |  97%
  |                                                                                                                   
  |==========================================================================================================   |  97%
  |                                                                                                                   
  |===========================================================================================================  |  98%
  |                                                                                                                   
  |============================================================================================================ |  99%
  |                                                                                                                   
  |=============================================================================================================| 100%
# summary(cluster, parameters = TRUE)

GPar.all$typ.all = cluster.all$classification
GPar.all$typ.in = cluster.in$classification
GPar.all$typ.out = cluster.out$classification

ggplot() +
  # Boundaries: +/- some separation from 0.5
  geom_contour(data = fine.grid, mapping = aes(x = x1, y = x2, z = dist, color = 'delta'), breaks = c(1)) +
  geom_contour(data = fine.grid, mapping = aes(x = x1, y = x2, z = cutof, color = 'cutof'), breaks = c(0.5)) +
  geom_contour(data = fine.grid, mapping = aes(x = x1, y = x2, z = rad, color = 'rad'), breaks = c(1)) +
  geom_contour(data = fine.grid, mapping = aes(x = x1, y = x2, z = ang, color = 'ang'), breaks = c(50)) +
  # Pareto frontier
  geom_smooth(data = GPar.front, mapping = aes(x = x1, y = x2, color = 'Pareto'), 
              level = 0.95, formula = (y~x), method = 'loess') + 
  # Clustering
  geom_point(data = GPar.all, mapping = aes(x = x1, y = x2, fill = as.factor(typ.all)), size = 2.5, shape = 21) +

  labs(x = expression('x'[1]), y = expression('x'[2]), 
       color = 'Acceptance Criteria', fill = 'Group', 
       subtitle = '(x1, x2, f1, f2)') +
  scale_color_manual(values = c('delta' = '#1b9e77', 'cutof' = '#d95f02', 
                                'rad' = '#7570b3', 'ang' = '#e7298a', 
                                'Pareto' = 'black'),
                     labels = c('delta' = expression(delta*' < 1'), 
                                'cutof' = expression('F'[1]^'*'*'< 1, F'[2]^'*'*'< 1'), 
                                'rad' = expression('r < 1'),
                                'ang' = expression('F'[1]*'and F'[2]*' Balance'),
                                'Pareto' = expression('Pareto Front')),
                     breaks = c('delta', 'cutof', 'rad', 'ang', 'Pareto')) +
  theme_classic() + #theme(legend.position = c(0.85, 0.75)) + 
  scale_x_continuous(expand = c(0, 0), limits = c(0, 5)) + scale_y_continuous(expand = c(0, 0), limits = c(0, 5)) +
  guides(colour = guide_legend(override.aes = list(fill = alpha('white', 1))))


ggplot() +
  # Boundaries: +/- some separation from 0.5
  geom_contour(data = fine.grid, mapping = aes(x = x1, y = x2, z = dist, color = 'delta'), breaks = c(1)) +
  geom_contour(data = fine.grid, mapping = aes(x = x1, y = x2, z = cutof, color = 'cutof'), breaks = c(0.5)) +
  geom_contour(data = fine.grid, mapping = aes(x = x1, y = x2, z = rad, color = 'rad'), breaks = c(1)) +
  geom_contour(data = fine.grid, mapping = aes(x = x1, y = x2, z = ang, color = 'ang'), breaks = c(50)) +
  # Pareto frontier
  geom_smooth(data = GPar.front, mapping = aes(x = x1, y = x2, color = 'Pareto'), 
              level = 0.95, formula = (y~x), method = 'loess') + 
  # Clustering
  geom_point(data = GPar.all, mapping = aes(x = x1, y = x2, fill = as.factor(typ.in)), size = 2.5, shape = 21) +

  labs(x = expression('x'[1]), y = expression('x'[2]), 
       color = 'Acceptance Criteria', fill = 'Group', 
       subtitle = '(x1, x2)') +
  scale_color_manual(values = c('delta' = '#1b9e77', 'cutof' = '#d95f02', 
                                'rad' = '#7570b3', 'ang' = '#e7298a', 
                                'Pareto' = 'black'),
                     labels = c('delta' = expression(delta*' < 1'), 
                                'cutof' = expression('F'[1]^'*'*'< 1, F'[2]^'*'*'< 1'), 
                                'rad' = expression('r < 1'),
                                'ang' = expression('F'[1]*'and F'[2]*' Balance'),
                                'Pareto' = expression('Pareto Front')),
                     breaks = c('delta', 'cutof', 'rad', 'ang', 'Pareto')) +
  theme_classic() + #theme(legend.position = c(0.85, 0.75)) + 
  scale_x_continuous(expand = c(0, 0), limits = c(0, 5)) + scale_y_continuous(expand = c(0, 0), limits = c(0, 5)) +
  guides(colour = guide_legend(override.aes = list(fill = alpha('white', 1))))


ggplot() +
  # Boundaries: +/- some separation from 0.5
  geom_contour(data = fine.grid, mapping = aes(x = x1, y = x2, z = dist, color = 'delta'), breaks = c(1)) +
  geom_contour(data = fine.grid, mapping = aes(x = x1, y = x2, z = cutof, color = 'cutof'), breaks = c(0.5)) +
  geom_contour(data = fine.grid, mapping = aes(x = x1, y = x2, z = rad, color = 'rad'), breaks = c(1)) +
  geom_contour(data = fine.grid, mapping = aes(x = x1, y = x2, z = ang, color = 'ang'), breaks = c(50)) +
  # Pareto frontier
  geom_smooth(data = GPar.front, mapping = aes(x = x1, y = x2, color = 'Pareto'), 
              level = 0.95, formula = (y~x), method = 'loess') + 
  # Clustering
  geom_point(data = GPar.all, mapping = aes(x = x1, y = x2, fill = as.factor(typ.out)), size = 2.5, shape = 21) +

  labs(x = expression('x'[1]), y = expression('x'[2]), 
       color = 'Acceptance Criteria', fill = 'Group', 
       subtitle = '(f1, f2)') +
  scale_color_manual(values = c('delta' = '#1b9e77', 'cutof' = '#d95f02', 
                                'rad' = '#7570b3', 'ang' = '#e7298a', 
                                'Pareto' = 'black'),
                     labels = c('delta' = expression(delta*' < 1'), 
                                'cutof' = expression('F'[1]^'*'*'< 1, F'[2]^'*'*'< 1'), 
                                'rad' = expression('r < 1'),
                                'ang' = expression('F'[1]*'and F'[2]*' Balance'),
                                'Pareto' = expression('Pareto Front')),
                     breaks = c('delta', 'cutof', 'rad', 'ang', 'Pareto')) +
  theme_classic() + #theme(legend.position = c(0.85, 0.75)) + 
  scale_x_continuous(expand = c(0, 0), limits = c(0, 5)) + scale_y_continuous(expand = c(0, 0), limits = c(0, 5)) +
  guides(colour = guide_legend(override.aes = list(fill = alpha('white', 1))))

The complete input (x1, x2, f1, f2) overcomplicates the system, providing 8 groups. One of these groups is clearly the Pareto frontier, but it does not include any points that are of similar performance. There are points to either side of it suggesting similar performance, but how far they extend is difficult to interpret.

The negative control (x1, x2) gives the expected result of largely grouping based on sampling density. This means the highly sampled Pareto frontier and local minimum are their own groups, and the rest of the space is divided based around the boundary between the highly sampled regions.

The output-only model gives the most useful results, as it groups the Pareto frontier inside of another high-performing group, which also includes the local optimum and the space spanning to it. It roughly lines up with the Pareto distance or threshold criteria; it does not appear to prioritize the angle or utopia distance. Showing only this model at finer resolution as it is the only relevant performing model

res = predict(object = cluster.out, newdata = fine.grid[c('f1', 'f2')])
fine.grid$cluster.out = res$classification
ggplot() +
  # Cluster results
  geom_point(data = fine.grid, mapping = aes(x = x1, y = x2, color = as.factor(cluster.out))) +
  # Boundaries: +/- some separation from 0.5
  geom_contour(data = fine.grid, mapping = aes(x = x1, y = x2, z = dist), color = 'black', breaks = c(1)) +
  geom_contour(data = fine.grid, mapping = aes(x = x1, y = x2, z = cutof), color = 'red', breaks = c(0.5)) +

  labs(x = expression('x'[1]), y = expression('x'[2]), 
       color = 'Group', fill = 'Group', 
       subtitle = '(f1, f2)') +
  theme_classic() + #theme(legend.position = c(0.85, 0.75)) + 
  scale_x_continuous(expand = c(0, 0), limits = c(0, 5)) + scale_y_continuous(expand = c(0, 0), limits = c(0, 5)) +
  guides(colour = guide_legend(override.aes = list(fill = alpha('white', 1))))


res = predict(object = cluster.all, newdata = fine.grid[c('x1', 'x2', 'f1', 'f2')])
fine.grid$cluster.all = res$classification
ggplot() +
  # Cluster results
  geom_point(data = fine.grid, mapping = aes(x = x1, y = x2, color = as.factor(cluster.all)), size = 4) +
  # Boundaries: +/- some separation from 0.5
  geom_contour(data = fine.grid, mapping = aes(x = x1, y = x2, z = dist), color = 'black', breaks = c(1)) +
  geom_contour(data = fine.grid, mapping = aes(x = x1, y = x2, z = cutof), color = 'red', breaks = c(0.5)) +

  labs(x = expression('x'[1]), y = expression('x'[2]), 
       color = 'Group', fill = 'Group', 
       subtitle = '(x1, x2, f1, f2)') +
  theme_classic() + #theme(legend.position = c(0.85, 0.75)) + 
  scale_x_continuous(expand = c(0, 0), limits = c(0, 5)) + scale_y_continuous(expand = c(0, 0), limits = c(0, 5)) +
  guides(colour = guide_legend(override.aes = list(fill = alpha('white', 1))))


ggplot() +
  # Cluster results
  geom_point(data = filter(fine.grid, f2 < 50, f1 < 200), mapping = aes(x = f1, y = f2, color = as.factor(cluster.all)), size = 2) +
  labs(x = expression('f'[1]), y = expression('f'[2]), 
       color = 'Group', fill = 'Group', 
       subtitle = '(x1, x2, f1, f2)') +
  theme_classic() +
  guides(colour = guide_legend(override.aes = list(fill = alpha('white', 1))))

The (f1, f2) model appears to give a cluster that is somewhere between a threshold cutoff and the Pareto distance cutoff. In contrast, the behavior of the (x1, x2, f1, f2) model provides many more groups, including splitting the Pareto front into about three different categories with no obvious analog to why the boundaries are where they are. It appears to roughly fit the same boundaries of Pareto distance or threshold cutoffs, but not very well. A rough approximation is that groups (5, 6) are the Pareto front, groups (2, 3, 7) make up the region close to the Pareto front, and (1, 4, 8) are the region far from the front.

The model for (f1, f2) is going to give the same conditional probabilities as the Pareto distance or threshold acceptance criteria based on this similarity in the shape of the boundary. The interesting result to interpret is the (x1, x2, f1, f2), particularly when marginalizing to (x1, x2). Assuming that calculating (f1, f2) is expensive, the best approximation is that found from the GP models used to find the Pareto front itself. These will give (f1, f2) as a bivariate Gaussian distribution, which can be sampled from to estimate the likelihood that it falls into the Pareto front, the region close to it, or the region far from it. This is achievable with a sequential Monte Carlo: given x1, sample x2 from the range, find the distribution of (f1, f2), sample (f1, f2), and solve the classification into the three groups.

f1.mod = fill.sample.mod(GPar.data = GPar.all, input.name = c('x1', 'x2'), output.name = 'f1')
f2.mod = fill.sample.mod(GPar.data = GPar.all, input.name = c('x1', 'x2'), output.name = 'f2')

x.rng = seq(from = 0, to = 5, length.out = 50)
nsamp.x = 100; nsamp.f = 100
gm.margin = data.frame()
for(x in x.rng){
  # x1
  # Sample x2
  inframe = data.frame(x1 = x, x2 = runif(n = nsamp.x, min = 0, max = 5))
  classes = c()
  for(n in 1:nrow(inframe)){
    # Estimate distribution for f1, f2
    f1.est = predict(object = f1.mod, newdata = inframe, type = 'UK')
    f2.est = predict(object = f2.mod, newdata = inframe, type = 'UK')
    test.frame = data.frame(x1 = inframe$x1, x2 = inframe$x2,
                            f1 = rnorm(n = nsamp.f, mean = f1.est$mean, sd = f1.est$sd),
                            f2 = rnorm(n = nsamp.f, mean = f2.est$mean, sd = f2.est$sd))
    res = predict(object = cluster.all, newdata = test.frame)
    classes = c(classes, res$classification)
  }
  classes = data.frame(group = classes)
  gm.margin = rbind(gm.margin, data.frame(x = x, var = 'x1', 
                         p.pare = nrow(filter(classes, group == 5 | group == 6))/(nsamp.f*nsamp.x), 
                         p.near = nrow(filter(classes, group == 2 | group == 3 | group == 7))/(nsamp.f*nsamp.x), 
                         p.dist = nrow(filter(classes, group == 1 | group == 4 | group == 8))/(nsamp.f*nsamp.x)))
  # x2
  # Sample x2
  inframe = data.frame(x2 = x, x1 = runif(n = nsamp.x, min = 0, max = 5))
  classes = c()
  for(n in 1:nrow(inframe)){
    # Estimate distribution for f1, f2
    f1.est = predict(object = f1.mod, newdata = inframe, type = 'UK')
    f2.est = predict(object = f2.mod, newdata = inframe, type = 'UK')
    test.frame = data.frame(x1 = inframe$x1, x2 = inframe$x2,
                            f1 = rnorm(n = nsamp.f, mean = f1.est$mean, sd = f1.est$sd),
                            f2 = rnorm(n = nsamp.f, mean = f2.est$mean, sd = f2.est$sd))
    res = predict(object = cluster.all, newdata = test.frame)
    classes = c(classes, res$classification)
  }
  classes = data.frame(group = classes)
  gm.margin = rbind(gm.margin, data.frame(x = x, var = 'x2', 
                         p.pare = nrow(filter(classes, group == 5 | group == 6))/(nsamp.f*nsamp.x), 
                         p.near = nrow(filter(classes, group == 2 | group == 3 | group == 7))/(nsamp.f*nsamp.x), 
                         p.dist = nrow(filter(classes, group == 1 | group == 4 | group == 8))/(nsamp.f*nsamp.x)))
}

write.csv(gm.margin, 'Margin_GM.csv')
gm.margin = read.csv('Margin_GM.csv')
gm.margin = data.frame(x = rep(gm.margin$x, 3),
                       var = rep(gm.margin$var, 3),
                       prob = c(gm.margin$p.pare, gm.margin$p.near, gm.margin$p.dist),
                       typ = c(rep('Pareto', nrow(gm.margin)), 
                               rep('Near-Pareto', nrow(gm.margin)), 
                               rep('Suboptimal', nrow(gm.margin))))
ggplot(gm.margin) +
  geom_path(mapping = aes(x = x, y = prob, color = typ)) +
  facet_wrap(~var, nrow = 2) +
  scale_color_brewer(palette = 'Dark2') + 
  labs(x = 'Input Value', y = 'Conditional Probability', color = 'Region')


gm.margin = read.csv('Margin_GM.csv')
gm.margin = data.frame(x = rep(gm.margin$x),
                       var = rep(gm.margin$var),
                       prob = gm.margin$p.pare + gm.margin$p.near,
                       typ = 'Optimal')
ggplot(gm.margin) +
  geom_path(mapping = aes(x = x, y = prob, color = typ)) +
  facet_wrap(~var, nrow = 2) +
  scale_color_brewer(palette = 'Dark2') + 
  labs(x = 'Input Value', y = 'Conditional Probability', color = 'Region')

The estimated probability of being optimal (Pareto front group or the near-Pareto group) is similar to that calculated through the other methods for the Pareto distance or threshold criteria. However, because of the numerous variables required to generate the model, a larger and more complicated sampling procedure is necessary for accurate estimates.

Overall:

LS0tCnRpdGxlOiAiUGF0dGVybiBSZWNvZ25pdGlvbiBNZXRob2QgQ29tcGFyaXNvbiIKYXV0aG9yOiAiSm9uYXRoYW4gQm91YWxhdm9uZyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKPCEtLSBvdXRwdXQ6ICAgIC0tPgo8IS0tICAgbWRfZG9jdW1lbnQ6IC0tPgo8IS0tICAgICB2YXJpYW50OiBtYXJrZG93bl9naXRodWIgLS0+CgojIERlc2NyaXB0aW9uClRoaXMgbm90ZWJvb2sgdGFrZXMgdGhlIGRhdGEgZ2VuZXJhdGVkIGJ5IHRoZSBzY3JpcHQgaW4gL0V4X1F1YXJ0aWMsIHdoaWNoIGRlc2NyaWJlcyB0aGUgbmV3bHkgZGV2ZWxvcGVkIG1ldGhvZCwgYW5kIGNvbXBhcmVzIGl0IGV4aXN0aW5nIGNsYXNzaWZpY2F0aW9uIGFsZ29yaXRobXMuIApUaGlzIG11bHRpLW9iamVjdGl2ZSBwcm9ibGVtIHdhcyBzZWxlY3RlZCBhcyBhIHNpbXBsZSBpbGx1c3RyYXRpb24sIGFzIGl0IG9ubHkgdGFrZXMgMiBpbnB1dHMgYW5kIHByb3ZpZGVzIDIgb3V0cHV0cywgYnV0IGhhcyB0aGUgY29tcGxpY2F0aW9uIG9mIGEgbG9jYWwgbWluaW11bSBhbmQgbWF4aW11bSBiZXNpZGVzIHRoZSBQYXJldG8gb3B0aW11bS4KClRoZSBwYXR0ZXJuIHJlY29nbml0aW9uIGFsZ29yaXRobXMgYXJlOgoqIFN1cHBvcnQgVmVjdG9yIE1hY2hpbmVzOiBleGFtcGxlIHN1cGVydmlzZWQgbGVhcm5pbmcgcHJvYmxlbQoqIEdhdXNzaWFuIG1peHR1cmUgbW9kZWxzOiBleGFtcGxlIHVuc3VwZXJ2aXNlZCBsZWFybmluZyBwcm9ibGVtCgpUaGVzZSBhcmUgY29tcGFyZWQgdG8gdGhlIHByb2Nlc3Mgb2YgY2hhcmFjdGVyaXppbmcgdGhlIG9wdGltYWwgaW5wdXRzIHVzaW5nIEdhdXNzaWFuIFByb2Nlc3NlcyByZWZpbmVkIGJ5IGFkYXB0aXZlIHNhbXBsaW5nLgoKRm9yIGVhc2Ugb2YgY2FsY3VsYXRpb24sIHRoZSBpdGVyYXRpdmUgcmVmaW5lbWVudCBvZiB0aGUgR1AgbW9kZWxzIGFyZSBub3QgaW5jbHVkZWQgaGVyZSwgbm9yIGFyZSB0aGUgY2FsY3VsYXRpb25zIG9mIHRoZSBjb25kaXRpb25hbCBwcm9iYWJpbGl0aWVzLgpUaGUgU1ZNIGFuZCBHTU0gbWV0aG9kcyBhcmUgdGVzdGVkIHdpdGggKDEpIHRoZSBkYXRhIGFmdGVyIGZpbmRpbmcgdGhlIFBhcmV0byBmcm9udCAoYmVmb3JlIHRoZSBuZXcgYWRhcHRpdmUgc2FtcGxpbmcgcHJvY2VzcyksICgyKSB0aGUgZGF0YSBhZnRlciBhZGFwdGl2ZSBzYW1wbGluZyByZWZpbmVtZW50LCBhbmQgKDMpIHRoZSBkYXRhIGFmdGVyIHRoZSBQYXJldG8gZnJvbnQgc2VhcmNoIHdpdGggYWRkaXRpb25hbCByYW5kb20gc2FtcGxpbmcgdG8gc2VlIGlmIHRoZSBuZXcgc2FtcGxpbmcgbWV0aG9kIGlzIHVzZWZ1bCBmb3IgdGhlc2Ugb3RoZXIgcHJvY2Vzc2VzIGFzIHdlbGwuCgpDb21wYXJpc29uIGFtb25nIHRoZSBzdXBlcnZpc2VkIG1ldGhvZHMgaXMgZG9uZSBieSBsb29raW5nIGF0IHRoZSBlcnJvciByYXRlIGFjcm9zcyB0aGUgZW50aXJlIHNwYWNlLiAKU2luY2UgdGhlIHRlc3QgZnVuY3Rpb24gaXMgYSBzaW1wbGUgcG9seW5vbWlhbCwgdGhlIHNvbHV0aW9uIG9mIHdoYXQgaXMgYWNjZXB0YWJsZSBjYW4gYmUgZm91bmQgZXhwbGljaXRseS4gCkZvciB0aGUgR1AgbWV0aG9kLCBzaW5jZSBhY2NlcHRhbmNlIGlzIGRlZmluZWQgYnkgYSBwcm9iYWJpbGl0eSwgdGhlIGVycm9yIHJhdGUgaXMgd2VpZ2h0ZWQgYnkgdGhlIHByb2JhYmlsaXR5IG9mIGFjY2VwdGFuY2UuIApGb3IgdGhlIFNWTSBtZXRob2QsIHRoZSBlcnJvciByYXRlIGlzIHNpbXBseSB0aGUgbnVtYmVyIG9mIGluY29ycmVjdGx5IGNhdGVnb3JpemVkIHBvaW50cyBkaXZpZGVkIGJ5IHRoZSB0b3RhbCBudW1iZXIgb2YgdGVzdCBwb2ludHMuCgpJbiBhZGRpdGlvbiB0byB0aGUgYWNjdXJhY3kgY29tcGFyaXNvbiwgYSBjb21wYXJpc29uIG9mIHRoZSBpbXBvcnRhbmNlIHJhbmtpbmcgbWV0cmljIGRldmVsb3BlZCBmb3IgdXNpbmcgR1AgaW4gY2xhc3NpZmljYXRpb24gcHJvYmxlbXMgd2lsbCBiZSBjb21wYXJlZCB0byBTaGFwbGV5IHZhbHVlcy4KQXMgd2l0aCB0aGUgYWNjdXJhY3kgY29tcGFyaXNvbiwgdGhpcyB3aWxsIGJlIHRlc3RlZCBhZ2FpbnN0IHRoZSBkYXRhIGJlZm9yZSByZWZpbmVtZW50LCBhZnRlciByZWZpbmVtZW50LCBvciBhZnRlciBhIHJhbmRvbSBzYW1wbGUuCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQpgYGAKCkNsZWFyIHRoZSB3b3Jrc3BhY2UgYW5kIGRlZmluZSB0aGUgZnVuY3Rpb25zLgoKYGBge3IgTG9hZCBQYWNrYWdlc30KIyBTZXR1cApybShsaXN0ID0gbHMoKSkKIyBWaXN1YWxpemF0aW9uCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShwYXRjaHdvcmspCiMgUGFyYWxsZWwgcHJvY2Vzc2luZwpsaWJyYXJ5KHBhcmFsbGVsKQpsaWJyYXJ5KGRvUGFyYWxsZWwpCiMgR2F1c3NpYW4gcHJvY2Vzc2VzCmxpYnJhcnkoR1BhcmV0bykKbGlicmFyeShEaWNlS3JpZ2luZykKbGlicmFyeShEaWNlT3B0aW0pCiMgT3B0aW1pemF0aW9uCmxpYnJhcnkoR0EpCiMgR2F1c3NpYW4gTWl4dHVyZSBNb2RlbHMKbGlicmFyeShtY2x1c3QpCiMgU3VwcG9ydCBWZWN0b3IgTWFjaGluZXMKbGlicmFyeShlMTA3MSkKYGBgCgpSZWxldmFudCBmdW5jdGlvbnMKClRoZSBxdWFydGljIGZ1bmN0aW9ucyB0byBvcHRpbWl6ZS4KCmBgYHtyIEZ1bmN0aW9uc30KIyBTZXQgKHgxLCB4Mikgb24gcmFuZ2Ugb2YgWzAsIDVdCgojIE9iamVjdGl2ZSBmdW5jdGlvbnMgYXJlIGJhc2VkIG9uIHRoZSBxdWFkcmF0aWMgZnVuY3Rpb25zIHVzZWQgcHJldmlvdXNseSwgYnV0IHdpdGggYW4gYWRkaXRpb25hbCBsb2NhbCBtaW5pbXVtCmYxID0gZnVuY3Rpb24oeDEsIHgyKXsKICByZXR1cm4oMjAqKHgxIC0gMC43NSleMiArIDE5MCArIDExLjU4KngyXjQgLSAxMTUuODUqeDJeMyArIDM4My4xMyp4Ml4yIC0gNDYzLjUwKngyKQp9CiMgVGhlIHNlY29uZCBvYmplY3RpdmUgZnVuY3Rpb24gaXMgYWxzbyBwYXJ0aWFsbHkgcm90YXRlZCBzbyB0aGUgbG9jYWwgb3B0aW11bSBpcyBub3QgcGVyZmVjdGx5IGFsaWduZWQKZjIgPSBmdW5jdGlvbih4MSwgeDIpewogICMgUmVtYXAgYm90aCB2YXJpYWJsZXM6IHJvdGF0ZSAzMCBkZWdyZWVzIGNvdW50ZXJjbG9ja3dpc2UKICBhbmcgPSAtcGkvMjQKICBuMSA9IHgxKmNvcyhhbmcpIC0geDIqc2luKGFuZykKICBuMiA9IHgxKnNpbihhbmcpICsgeDIqY29zKGFuZykKICAjIHJldHVybigoeDEgLSAyLjUpXjIgKyA4MCArIDEuNzc4KngyXjQgLSAyMCp4Ml4zICsgNzguNTczKngyXjIgLSAxMjQuNjY0KngyKQogIHJldHVybigobjEgLSAyLjUpXjIgKyA4MCArIDEuNzc4Km4yXjQgLSAyMCpuMl4zICsgNzguNTczKm4yXjIgLSAxMjQuNjY0Km4yKQp9CgoKIyBVc2luZyBHUGFyZXRvOiBOZWVkIGlucHV0cyBhbmQgb3V0cHV0cyBhcyB2ZWN0b3JzL21hdHJpY2VzIG5vdCBhcyBkYXRhZnJhbWVzLiBDb2Fyc2UgaW5pdGlhbCBkZXNpZ24KZnVuID0gZnVuY3Rpb24oeCl7CiAgeDEgPSB4WzFdOyB4MiA9IHhbMl0KICByZXR1cm4oYyhmMSh4MSwgeDIpLCBmMih4MSwgeDIpKSkKfQoKYGBgCgpOb3JtYWxpemF0aW9uLXJlbGF0ZWQgZnVuY3Rpb25zCmBgYHtyIEZ1bmN0aW9ucyBwYXJ0IDJ9CiMgTm9ybWFsaXplZCBvYmplY3RpdmUgZnVuY3Rpb25zCm4ub2JqID0gZnVuY3Rpb24oR1Bhci5kYXRhLCBHUGFyLmZyb250KXsKICAjIEdpdmVuIGRhdGFmcmFtZXMgdGhhdCBkZXNjcmliZSB0aGUgZW50aXJlIGRhdGFzZXQgYW5kIHRoZSBmcm9udCwgZmluZCB0aGUgbm9ybWFsaXplZCAoeCx5KQogICMgT2JqZWN0aXZlIGZ1bmN0aW9ucyBhcmUgbmFtZWQgJ2YxJyBhbmQgJ2YyJwogIAogICMgTm9ybWFsaXplIHRoZSBvYmplY3RpdmUgb3V0cHV0cyBzbyB0aGF0IHRoZSB1dG9waWEgcG9pbnQgaXMgKDAsMCkgYW5kIHRoZSBuYWRpciBwb2ludCBpcyAoMSwxKQogIGYxLnVwID0gR1Bhci5mcm9udCRmMVt3aGljaC5taW4oR1Bhci5mcm9udCRmMildCiAgZjIudXAgPSBHUGFyLmZyb250JGYyW3doaWNoLm1pbihHUGFyLmZyb250JGYxKV0KICBHUGFyLmRhdGEkZjEubm9ybSA9IChHUGFyLmRhdGEkZjEgLSBtaW4oR1Bhci5mcm9udCRmMSkpLyhmMS51cCAtIG1pbihHUGFyLmZyb250JGYxKSkKICBHUGFyLmRhdGEkZjIubm9ybSA9IChHUGFyLmRhdGEkZjIgLSBtaW4oR1Bhci5mcm9udCRmMikpLyhmMi51cCAtIG1pbihHUGFyLmZyb250JGYyKSkKCiAgcmV0dXJuKEdQYXIuZGF0YSkKfQoKIyBDYWxjdWxhdGUgdGhlIG5vcm1hbGl6ZWQgZGlzdGFuY2UKbi5kaXN0ID0gZnVuY3Rpb24oZjEubm9ybSwgZjIubm9ybSwgR1Bhci5mcm9udCl7CiAgIyBHaXZlbiB0aGUgbm9ybWFsaXplZCBjb29yZGluYXRlcyAoZjEubm9ybSwgZjIubm9ybSkgYW5kIHRoZSBQYXJldG8gZnJvbnRpZXIgZXN0aW1hdGUsCiAgIyBmaW5kIHRoZSBkaXN0YW5jZSBhbG9uZyB0aGUgY29uc3RhbnQgZjIvZjEgcmF0aW8gbGluZQogIAogICMgRGV0ZXJtaW5lIHRoZSB0d28gcG9pbnRzIG9uIHRoZSBQYXJldG8gZnJvbnQgdGhhdCBkZWZpbmUgdGhlIHJlbGV2YW50IHNlZ21lbnQKICBHUGFyLmZyb250JHRoZXRhID0gYXRhbihHUGFyLmZyb250JGYyLm5vcm0gLyBHUGFyLmZyb250JGYxLm5vcm0pCiAgaWYoZjEubm9ybSA8IDApe2YxLm5vcm0gPSAwfQogIGlmKGYyLm5vcm0gPCAwKXtmMi5ub3JtID0gMH0KICByYXRpbyA9IGF0YW4oZjIubm9ybS9mMS5ub3JtKQogIAogICMgQ2hlY2sgaWYgdGhlIGFuZ2xlIGlzIHRoZSBzYW1lIGFzIGEgcG9pbnQgb24gdGhlIFBhcmV0byBmcm9udAogIGlmKGFueShhYnMocmF0aW8gLSBHUGFyLmZyb250JHRoZXRhKSA8IDFlLTUpKXsKICAgIHBvcyA9IHdoaWNoLm1pbihhYnMocmF0aW8gLSBHUGFyLmZyb250JHRoZXRhKSkKICAgIFBhci54ID0gR1Bhci5mcm9udCRmMS5ub3JtW3Bvc10KICAgIFBhci55ID0gR1Bhci5mcm9udCRmMi5ub3JtW3Bvc10KICB9IGVsc2V7ICMgT3RoZXJ3aXNlLCB0d28gcG9pbnRzIGFyZSBuZWVkZWQgZm9yIGxpbmVhciBpbnRlcnBvbGF0aW9uCiAgICAjIEJyZWFrIHRoZSBkYXRhZnJhbWUgaW50byB0aGV0YSBhYm92ZSBhbmQgYmVsb3cKICAgIFBhci5hYm92ZSA9IEdQYXIuZnJvbnRbR1Bhci5mcm9udCR0aGV0YSAtIHJhdGlvID4gMCxdCiAgICBQYXIuYmVsb3cgPSBHUGFyLmZyb250W0dQYXIuZnJvbnQkdGhldGEgLSByYXRpbyA8IDAsXQogICAgIyBGaW5kIHRoZSBwb2ludCBjbG9zZXN0IHRvIHRoZSBhbmdsZQogICAgcG9zLmFib3ZlID0gd2hpY2gubWluKGFicyhyYXRpbyAtIFBhci5hYm92ZSR0aGV0YSkpCiAgICBwb3MuYmVsb3cgPSB3aGljaC5taW4oYWJzKHJhdGlvIC0gUGFyLmJlbG93JHRoZXRhKSkKICAgICMgTGluZWFyIGludGVycG9sYXRpb24KICAgIGxuLnggPSBjKFBhci5hYm92ZSRmMS5ub3JtW3Bvcy5hYm92ZV0sIFBhci5iZWxvdyRmMS5ub3JtW3Bvcy5iZWxvd10pCiAgICBsbi55ID0gYyhQYXIuYWJvdmUkZjIubm9ybVtwb3MuYWJvdmVdLCBQYXIuYmVsb3ckZjIubm9ybVtwb3MuYmVsb3ddKQogICAgc2xwID0gZGlmZihsbi55KS9kaWZmKGxuLngpCiAgICAjIEZpbmQgdGhlIHBvaW50IG9uIHRoZSBzZWdtZW50IHdpdGggdGhlIHNhbWUgYW5nbGUsIGllLiB0aGUgc2FtZSByYXRpby4KICAgICMgU29sdmluZyB3aXRoIHRoaXMgY29uc3RyYWludCBoYXMgYW5hbHl0aWNhbCBzb2x1dGlvbjoKICAgIFBhci54ID0gKGxuLnlbMV0gLSBzbHAqbG4ueFsxXSkgLyAoZjIubm9ybS9mMS5ub3JtIC0gc2xwKQogICAgUGFyLnkgPSBzbHAqKFBhci54IC0gbG4ueFsxXSkgKyBsbi55WzFdCiAgfQogIAogICMgTGluZWFyIGRpc3RhbmNlIHRvIHRoZSBmcm9udCBpcyB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIGRpc3RhbmNlcyB0byB0aGUgb3JpZ2luCiAgZGlzdCA9IHNxcnQoZjEubm9ybV4yICsgZjIubm9ybV4yKSAtIHNxcnQoUGFyLnheMiArIFBhci55XjIpCiAgcmV0dXJuKGRpc3QpCn0KYGBgCgpHYXVzc2lhbiBwcm9jZXNzIHBhcmFtZXRlciB0dW5pbmcKYGBge3J9CmZpbGwuc2FtcGxlLm1vZCA9IGZ1bmN0aW9uKEdQYXIuZGF0YSwgaW5wdXQubmFtZSwgb3V0cHV0Lm5hbWUpewogICMgQ2FsY3VsYXRlIHRoZSBHUCBtb2RlbCB0byB1c2UuIAogICMgVXNpbmcgdGhlIGttIGZ1bmN0aW9uLCBidXQgYXBwbGllcyBjaGVja3Mgb24gdGhlIHN5c3RlbSB0byBtYWtlIHN1cmUgdGhhdCAKICAjIHRoZSBtb2RlbCB1bmNlcnRhaW50eSBtYXRjaGVzIGV4cGVjdGF0aW9ucyBiYXNlZCBvbiBHUCwgaWUuIGl0IGRpZCBub3QKICAjIGZhaWwgdG8gY29udmVyZ2UuCiAgCiAgIyBCYXNlZCBvbiB0ZXN0aW5nLCB0aGUgbW9kZWwgaXMgYmFkIHdoZW4gdGhlIDEwJSBwZXJjZW50aWxlIGFuZCA5MCUgcGVyY2VudGlsZSAKICAjIG9mIHRoZSBzdGFuZGFyZCBkZXZpYXRpb24gYXJlIG9mIHRoZSBzYW1lIG9yZGVyIG9mIG1hZ25pdHVkZS4gVGhpcyBpcyBlYXNpZXN0CiAgIyBjaGVja2VkIGlmIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIDEwdGggYW5kIDkwdGggcGVyY2VudGlsZQogICMgaXMgbGFyZ2VyIHRoYW4gdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgMjV0aCBhbmQgNzV0aC4KICBwdDEwID0gMTsgcHQ5MCA9IDE7IHB0MjUgPSAxOyBwdDc1ID0gMQogIHdoaWxlKGxvZzEwKHB0OTAvcHQxMCkgPD0gbG9nMTAocHQ3NS9wdDI1KSl7CiAgICBtb2Qub3V0ID0ga20oZGVzaWduID0gR1Bhci5kYXRhWywgaW5wdXQubmFtZV0sIHJlc3BvbnNlID0gR1Bhci5kYXRhWywgb3V0cHV0Lm5hbWVdLCAKICAgICAgICAgICAgICAgICBjb3Z0eXAgPSAnZ2F1c3MnLCAjIEdhdXNzaWFuIHVuY2VydGFpbnR5CiAgICAgICAgICAgICAgICAgb3B0aW0ubWV0aG9kID0gJ2dlbicsICMgR2VuZXRpYyBhbGdvcml0aG0gb3B0aW1pemF0aW9uCiAgICAgICAgICAgICAgICAgY29udHJvbCA9IGxpc3QodHJhY2UgPSBGQUxTRSwgIyBUdXJuIG9mZiB0cmFja2luZyB0byBzaW1wbGlmeSBvdXRwdXQKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwb3Auc2l6ZSA9IDUwLCAjIEluY3JlYXNlIHJvYnVzdG5lc3MKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXguZ2VuZXJhdGlvbnMgPSA0MDApLCAjIFNvbWUgY29udmVyZ2VuY2UgaXNzdWVzCiAgICAgICAgICAgICAgICAgbnVnZ2V0ID0gMWUtNiwgIyBBdm9pZCBlaWdlbnZhbHVlcyBvZiAwCiAgICAgICAgICAgICAgICAgKQogICAgCiAgICAjIFJhbmRvbWx5IHNhbXBsZSAyMDAgcG9pbnRzIGZyb20gdGhlIHNlYXJjaCBzcGFjZQogICAgcHQgPSAyMDA7IGkgPSAxCiAgICBsaW1zID0gcmFuZ2UoR1Bhci5kYXRhWyxpbnB1dC5uYW1lW2ldXSkKICAgIHNhbXAgPSBkYXRhLmZyYW1lKHJ1bmlmKG4gPSBwdCwgbWluID0gbGltc1sxXSwgbWF4ID0gbGltc1syXSkpCiAgICBmb3IoaSBpbiAyOmxlbmd0aChpbnB1dC5uYW1lKSl7CiAgICAgIGxpbXMgPSByYW5nZShHUGFyLmRhdGFbLGlucHV0Lm5hbWVbaV1dKQogICAgICBzYW1wWyxpXSA9IHJ1bmlmKG4gPSBwdCwgbWluID0gbGltc1sxXSwgbWF4ID0gbGltc1syXSkKICAgIH0KICAgIG5hbWVzKHNhbXApID0gaW5wdXQubmFtZQogICAgCiAgICAjIEZpbmQgbW9kZWwgb3V0cHV0IHRvIGZpbmQgdGhlIHBlcmNlbnRpbGUgcmFua3MgZm9yIHRoaXMgaXRlcmF0aW9uCiAgICByZXMgPSBwcmVkaWN0KG9iamVjdCA9IG1vZC5vdXQsIG5ld2RhdGEgPSBzYW1wLCB0eXBlID0gJ1VLJykKICAgIHB0MTAgPSBxdWFudGlsZShyZXMkc2QsIDAuMTApOyBwdDkwID0gcXVhbnRpbGUocmVzJHNkLCAwLjkwKQogICAgcHQyNSA9IHF1YW50aWxlKHJlcyRzZCwgMC4yNSk7IHB0NzUgPSBxdWFudGlsZShyZXMkc2QsIDAuNzUpCiAgfQogIHJldHVybihtb2Qub3V0KQp9CmBgYAoKCiMgQ29tcGFyaXNvbiBvZiBHUC1iYXNlZCBCb3VuZGFyaWVzIHdpdGggRGlmZmVyZW50IEFjY2VwdGFuY2UgQ3JpdGVyaWEKCkNvbXBhcmlzb24gb2YgdGhlIGJvdW5kYXJpZXMgdG8gc2hvdyBob3cgY2hhbmdpbmcgdGhlIGFjY2VwdGFuY2UgY3JpdGVyaWEgY2hhbmdlcyB0aGUgc2hhcGUgb2YgdGhlIG5lYXItUGFyZXRvIHNldC4gU2hvd2Nhc2VzIHRoZSByb2J1c3RuZXNzIHRvIGRpZmZlcmVudCBzZWxlY3Rpb24gY3JpdGVyaWEsIGluZGljYXRpbmcgZmxleGliaWxpdHkgaW4gdGhlIGRlc2lnbiBvYmplY3RpdmVzLgoKYGBge3IgR1A6IExvYWRpbmcgRGF0YX0KIyMgTG9hZGluZwojIExvYWQgZGF0YXNldHMgZm9yIG9idGFpbmluZyB0aGUgcmVmaW5lZCBwcm9iYWJpbGl0eSBmdW5jdGlvbnMKZGF0YS5kZWx0YSA9IHJlYWQuY3N2KCcuLi9FeF9RdWFydGljL0dQYXJfQWNjZXB0X0RlbHRhMS5jc3YnKQpkYXRhLmN1dG9mID0gcmVhZC5jc3YoJy4uL0V4X1F1YXJ0aWMvR1Bhcl9BY2NlcHRfVGhyZXNob2xkLmNzdicpCmRhdGEucmFkYW4gPSByZWFkLmNzdignLi4vRXhfUXVhcnRpYy9HUGFyX0FjY2VwdF9SYWRpdXMuY3N2JykKIyBMb2FkIGRhdGFzZXRzIHByaW9yIHRvIHJlZmluZW1lbnQKZGF0YS5wYXJldCA9IHJlYWQuY3N2KCcuLi9FeF9RdWFydGljL0dQYXJfYWxsX3N0YXJ0LmNzdicpCmRhdGEucGFyZXQkcmFkID0gc3FydChkYXRhLnBhcmV0JGYxLm5vcm1eMiArIGRhdGEucGFyZXQkZjIubm9ybV4yKQojIENvbXBhcmUgdG8gdGhlIGVzdGltYXRlIG9mIHRoZSBQYXJldG8gZnJvbnRpZXIKR1Bhci5mcm9udCA9IHJlYWQuY3N2KGZpbGUgPSAnLi4vRXhfUXVhcnRpYy9HUGFyX2ZudF9zdGFydC5jc3YnKQoKIyAjIEFkZCBhIHJhbmRvbSBzYW1wbGVzIHRvIHNpbXVsYXRlIHRoZSBlZmZlY3Qgb2Ygc2FtcGxlIHNpemUgcmF0aGVyIHRoYW4gYWRhcHRpdmUgc2FtcGxpbmcKIyBuc2FtcCA9IG1heChucm93KGRhdGEuZGVsdGEpLCBucm93KGRhdGEuY3V0b2YpLCBucm93KGRhdGEucmFkYW4pKSAtIG5yb3coZGF0YS5wYXJldCkKIyBkYXRhLnJhbmRvID0gZGF0YS5mcmFtZSh4MSA9IHJ1bmlmKG4gPSBuc2FtcCwgbWluID0gMCwgbWF4ID0gNSksCiMgICAgICAgICAgICAgICAgICAgICAgICAgeDIgPSBydW5pZihuID0gbnNhbXAsIG1pbiA9IDAsIG1heCA9IDUpKQojIGRhdGEucmFuZG8kZjEgPSBmMSh4MSA9IGRhdGEucmFuZG8keDEsIHgyID0gZGF0YS5yYW5kbyR4MikKIyBkYXRhLnJhbmRvJGYyID0gZjIoeDEgPSBkYXRhLnJhbmRvJHgxLCB4MiA9IGRhdGEucmFuZG8keDIpCiMgIyBGaWxsIGluIHRoZSByZW1haW5pbmcgY2FsY3VsYXRpb25zOiBub3JtYWxpemVkIG91dHB1dHMsIGRpc3RhbmNlLCB0aGV0YSwgb3JkZXIKIyBkYXRhLnJhbmRvID0gbi5vYmooR1Bhci5kYXRhID0gZGF0YS5yYW5kbywgR1Bhci5mcm9udCA9IEdQYXIuZnJvbnQpCiMgY2wgPC0gbWFrZUNsdXN0ZXIoMikKIyByZWdpc3RlckRvUGFyYWxsZWwoY2wpCiMgZGlzdCA9IGZvcmVhY2gocm93ID0gMTpucm93KGRhdGEucmFuZG8pKSAlZG9wYXIlCiMgICBuLmRpc3QoZjEubm9ybSA9IGRhdGEucmFuZG8kZjEubm9ybVtyb3ddLCBmMi5ub3JtID0gZGF0YS5yYW5kbyRmMi5ub3JtW3Jvd10sIEdQYXIuZnJvbnQgPSBHUGFyLmZyb250KQojIHN0b3BDbHVzdGVyKGNsKQojIGRhdGEucmFuZG8kZGlzdCA9IHVubGlzdChkaXN0KQojIGRhdGEucmFuZG8kcmFkID0gc3FydChkYXRhLnJhbmRvJGYxLm5vcm1eMiArIGRhdGEucmFuZG8kZjIubm9ybV4yKQojIGRhdGEucmFuZG8kdGhldGEgPSBhdGFuKGRhdGEucmFuZG8kZjIubm9ybS9kYXRhLnJhbmRvJGYxLm5vcm0pKjE4MC9waSoxMC85CiMgZGF0YS5yYW5kbyRvcmRlciA9IHNlcShmcm9tID0gbWF4KGRhdGEucGFyZXQkb3JkZXIpICsgMSwgdG8gPSBtYXgoZGF0YS5wYXJldCRvcmRlcikgKyBuc2FtcCwgYnkgPSAxKQojIGRhdGEucmFuZG8gPSByYmluZChkYXRhLnBhcmV0WywgbmFtZXMoZGF0YS5wYXJldCkgJWluJSBuYW1lcyhkYXRhLnJhbmRvKV0sIGRhdGEucmFuZG8pCiMgCiMgIyBTdG9yZSB0aGUgcmFuZG9tIGRhdGEKIyB3cml0ZS5jc3YoZGF0YS5yYW5kbywgZmlsZSA9ICdHUGFyX1JhbmRvbS5jc3YnKQpkYXRhLnJhbmRvID0gcmVhZC5jc3YoZmlsZSA9ICdHUGFyX1JhbmRvbS5jc3YnKQoKIyMKIyBHcmlkIG9mIHRoZSByZWxldmFudCByZWdpb24gdG8gdmlzdWFsaXplIGFuZCBjb21wYXJlCmxvd2VyID0gYygwLCAwKTsgdXBwZXIgPSBjKDUsNSk7IGdyaWQuc3ogPSAxMDAKZmluZS5ncmlkID0gZXhwYW5kLmdyaWQoeDEgPSBzZXEoZnJvbSA9IGxvd2VyWzFdLCB0byA9IHVwcGVyWzFdLCBsZW5ndGgub3V0ID0gZ3JpZC5zeiksIAogICAgICAgICAgICAgICAgICAgICAgICB4MiA9IHNlcShmcm9tID0gbG93ZXJbMl0sIHRvID0gdXBwZXJbMl0sIGxlbmd0aC5vdXQgPSBncmlkLnN6KSkKZmluZS5ncmlkID0gZmluZS5ncmlkWywxOjJdCm5hbWVzKGZpbmUuZ3JpZCkgPSBjKCd4MScsICd4MicpCiMgQ2FsY3VsYXRlIHRoZSBhY3R1YWwgcmVzdWx0cwpmaW5lLmdyaWQkZjEgPSBmMSh4MSA9IGZpbmUuZ3JpZCR4MSwgeDIgPSBmaW5lLmdyaWQkeDIpCmZpbmUuZ3JpZCRmMiA9IGYyKHgxID0gZmluZS5ncmlkJHgxLCB4MiA9IGZpbmUuZ3JpZCR4MikKZmluZS5ncmlkID0gbi5vYmooR1Bhci5kYXRhID0gZmluZS5ncmlkLCBHUGFyLmZyb250ID0gR1Bhci5mcm9udCkKZmluZS5ncmlkJGRpc3QgPSBOYU4KZm9yKHJvdyBpbiAxOm5yb3coZmluZS5ncmlkKSl7CiAgZmluZS5ncmlkJGRpc3Rbcm93XSA9IG4uZGlzdChmMS5ub3JtID0gZmluZS5ncmlkJGYxLm5vcm1bcm93XSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmMi5ub3JtID0gZmluZS5ncmlkJGYyLm5vcm1bcm93XSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBHUGFyLmZyb250ID0gR1Bhci5mcm9udCkKfQpmaW5lLmdyaWQkYW5nID0gYXRhbihmaW5lLmdyaWQkZjIubm9ybS9maW5lLmdyaWQkZjEubm9ybSkqMTgwL3BpKjEwLzkKZmluZS5ncmlkJHJhZCA9IHNxcnQoZmluZS5ncmlkJGYxLm5vcm1eMiArIGZpbmUuZ3JpZCRmMi5ub3JtXjIpCmBgYAoKYGBge3IgR1A6IEdlbmVyYXRpbmcgTW9kZWxzfQojIwojIE5vcm1hbGl6ZWQgZGlzdGFuY2UKZmluZS5ncmlkJGRlbHRhID0gMApmaW5lLmdyaWQkZGVsdGFbZmluZS5ncmlkJGRpc3QgPD0gMV0gPSAxCgptb2QuZGlzdC5wYXJldCA9IGZpbGwuc2FtcGxlLm1vZChHUGFyLmRhdGEgPSBkYXRhLnBhcmV0LCBpbnB1dC5uYW1lID0gYygneDEnLCAneDInKSwgb3V0cHV0Lm5hbWUgPSAnZGlzdCcpCm1vZC5kaXN0LmFkYXB0ID0gZmlsbC5zYW1wbGUubW9kKEdQYXIuZGF0YSA9IGRhdGEuZGVsdGEsIGlucHV0Lm5hbWUgPSBjKCd4MScsICd4MicpLCBvdXRwdXQubmFtZSA9ICdkaXN0JykKbW9kLmRpc3QucmFuZG8gPSBmaWxsLnNhbXBsZS5tb2QoR1Bhci5kYXRhID0gZGF0YS5yYW5kbywgaW5wdXQubmFtZSA9IGMoJ3gxJywgJ3gyJyksIG91dHB1dC5uYW1lID0gJ2Rpc3QnKQoKcmVzID0gcHJlZGljdChvYmplY3QgPSBtb2QuZGlzdC5wYXJldCwgbmV3ZGF0YSA9IGZpbmUuZ3JpZFssYygneDEnLCAneDInKV0sIHR5cGUgPSAiVUsiKQpmaW5lLmdyaWQkcHJvYi5kZWx0YS5wYXJldCA9IHBub3JtKHEgPSAwLCBtZWFuID0gcmVzJG1lYW4gLSAxLCBzZCA9IHJlcyRzZCkKcmVzID0gcHJlZGljdChvYmplY3QgPSBtb2QuZGlzdC5hZGFwdCwgbmV3ZGF0YSA9IGZpbmUuZ3JpZFssYygneDEnLCAneDInKV0sIHR5cGUgPSAiVUsiKQpmaW5lLmdyaWQkcHJvYi5kZWx0YS5hZGFwdCA9IHBub3JtKHEgPSAwLCBtZWFuID0gcmVzJG1lYW4gLSAxLCBzZCA9IHJlcyRzZCkKcmVzID0gcHJlZGljdChvYmplY3QgPSBtb2QuZGlzdC5yYW5kbywgbmV3ZGF0YSA9IGZpbmUuZ3JpZFssYygneDEnLCAneDInKV0sIHR5cGUgPSAiVUsiKQpmaW5lLmdyaWQkcHJvYi5kZWx0YS5yYW5kbyA9IHBub3JtKHEgPSAwLCBtZWFuID0gcmVzJG1lYW4gLSAxLCBzZCA9IHJlcyRzZCkKCiMjCiMgVGhyZXNob2xkIGN1dG9mZgpmaW5lLmdyaWQkY3V0b2YgPSAwCmZpbmUuZ3JpZCRjdXRvZltmaW5lLmdyaWQkZjEubm9ybSA8PSAxICYgZmluZS5ncmlkJGYyLm5vcm0gPD0gMV0gPSAxCgptb2QuZjEucGFyZXQgPSBmaWxsLnNhbXBsZS5tb2QoR1Bhci5kYXRhID0gZGF0YS5wYXJldCwgaW5wdXQubmFtZSA9IGMoJ3gxJywgJ3gyJyksIG91dHB1dC5uYW1lID0gJ2YxLm5vcm0nKQptb2QuZjIucGFyZXQgPSBmaWxsLnNhbXBsZS5tb2QoR1Bhci5kYXRhID0gZGF0YS5wYXJldCwgaW5wdXQubmFtZSA9IGMoJ3gxJywgJ3gyJyksIG91dHB1dC5uYW1lID0gJ2YyLm5vcm0nKQptb2QuZjEuYWRhcHQgPSBmaWxsLnNhbXBsZS5tb2QoR1Bhci5kYXRhID0gZGF0YS5jdXRvZiwgaW5wdXQubmFtZSA9IGMoJ3gxJywgJ3gyJyksIG91dHB1dC5uYW1lID0gJ2YxLm5vcm0nKQptb2QuZjIuYWRhcHQgPSBmaWxsLnNhbXBsZS5tb2QoR1Bhci5kYXRhID0gZGF0YS5jdXRvZiwgaW5wdXQubmFtZSA9IGMoJ3gxJywgJ3gyJyksIG91dHB1dC5uYW1lID0gJ2YyLm5vcm0nKQptb2QuZjEucmFuZG8gPSBmaWxsLnNhbXBsZS5tb2QoR1Bhci5kYXRhID0gZGF0YS5yYW5kbywgaW5wdXQubmFtZSA9IGMoJ3gxJywgJ3gyJyksIG91dHB1dC5uYW1lID0gJ2YxLm5vcm0nKQptb2QuZjIucmFuZG8gPSBmaWxsLnNhbXBsZS5tb2QoR1Bhci5kYXRhID0gZGF0YS5yYW5kbywgaW5wdXQubmFtZSA9IGMoJ3gxJywgJ3gyJyksIG91dHB1dC5uYW1lID0gJ2YyLm5vcm0nKQoKcmVzMSA9IHByZWRpY3Qob2JqZWN0ID0gbW9kLmYxLnBhcmV0LCBuZXdkYXRhID0gZGF0YS5mcmFtZSh4MSA9IGZpbmUuZ3JpZCR4MSwgeDIgPSBmaW5lLmdyaWQkeDIpLCB0eXBlID0gIlVLIikKcmVzMiA9IHByZWRpY3Qob2JqZWN0ID0gbW9kLmYyLnBhcmV0LCBuZXdkYXRhID0gZGF0YS5mcmFtZSh4MSA9IGZpbmUuZ3JpZCR4MSwgeDIgPSBmaW5lLmdyaWQkeDIpLCB0eXBlID0gIlVLIikKZmluZS5ncmlkJHByb2IuY3V0b2YucGFyZXQgPSBwbm9ybShxID0gMCwgbWVhbiA9IHJlczEkbWVhbiAtIDEsIHNkID0gcmVzMSRzZCkgKiAKICBwbm9ybShxID0gMCwgbWVhbiA9IHJlczIkbWVhbiAtIDEsIHNkID0gcmVzMiRzZCkKcmVzMSA9IHByZWRpY3Qob2JqZWN0ID0gbW9kLmYxLmFkYXB0LCBuZXdkYXRhID0gZGF0YS5mcmFtZSh4MSA9IGZpbmUuZ3JpZCR4MSwgeDIgPSBmaW5lLmdyaWQkeDIpLCB0eXBlID0gIlVLIikKcmVzMiA9IHByZWRpY3Qob2JqZWN0ID0gbW9kLmYyLmFkYXB0LCBuZXdkYXRhID0gZGF0YS5mcmFtZSh4MSA9IGZpbmUuZ3JpZCR4MSwgeDIgPSBmaW5lLmdyaWQkeDIpLCB0eXBlID0gIlVLIikKZmluZS5ncmlkJHByb2IuY3V0b2YuYWRhcHQgPSBwbm9ybShxID0gMCwgbWVhbiA9IHJlczEkbWVhbiAtIDEsIHNkID0gcmVzMSRzZCkgKiAKICBwbm9ybShxID0gMCwgbWVhbiA9IHJlczIkbWVhbiAtIDEsIHNkID0gcmVzMiRzZCkKcmVzMSA9IHByZWRpY3Qob2JqZWN0ID0gbW9kLmYxLnJhbmRvLCBuZXdkYXRhID0gZGF0YS5mcmFtZSh4MSA9IGZpbmUuZ3JpZCR4MSwgeDIgPSBmaW5lLmdyaWQkeDIpLCB0eXBlID0gIlVLIikKcmVzMiA9IHByZWRpY3Qob2JqZWN0ID0gbW9kLmYyLnJhbmRvLCBuZXdkYXRhID0gZGF0YS5mcmFtZSh4MSA9IGZpbmUuZ3JpZCR4MSwgeDIgPSBmaW5lLmdyaWQkeDIpLCB0eXBlID0gIlVLIikKZmluZS5ncmlkJHByb2IuY3V0b2YucmFuZG8gPSBwbm9ybShxID0gMCwgbWVhbiA9IHJlczEkbWVhbiAtIDEsIHNkID0gcmVzMSRzZCkgKiAKICBwbm9ybShxID0gMCwgbWVhbiA9IHJlczIkbWVhbiAtIDEsIHNkID0gcmVzMiRzZCkKCiMjCiMgUmFkaXVzLWFuZ2xlCmZpbmUuZ3JpZCRyYWRhbiA9IDAKZmluZS5ncmlkJHJhZGFuW2ZpbmUuZ3JpZCRyYWQgPD0gMSAmIGZpbmUuZ3JpZCRhbmcgPiAyMF0gPSAxCgptb2QucmFkLnBhcmV0ID0gZmlsbC5zYW1wbGUubW9kKEdQYXIuZGF0YSA9IGRhdGEucGFyZXQsIGlucHV0Lm5hbWUgPSBjKCd4MScsICd4MicpLCBvdXRwdXQubmFtZSA9ICdyYWQnKQptb2QuYW5nLnBhcmV0ID0gZmlsbC5zYW1wbGUubW9kKEdQYXIuZGF0YSA9IGRhdGEucGFyZXQsIGlucHV0Lm5hbWUgPSBjKCd4MScsICd4MicpLCBvdXRwdXQubmFtZSA9ICd0aGV0YScpCm1vZC5yYWQuYWRhcHQgPSBmaWxsLnNhbXBsZS5tb2QoR1Bhci5kYXRhID0gZGF0YS5yYWRhbiwgaW5wdXQubmFtZSA9IGMoJ3gxJywgJ3gyJyksIG91dHB1dC5uYW1lID0gJ3JhZCcpCm1vZC5hbmcuYWRhcHQgPSBmaWxsLnNhbXBsZS5tb2QoR1Bhci5kYXRhID0gZGF0YS5yYWRhbiwgaW5wdXQubmFtZSA9IGMoJ3gxJywgJ3gyJyksIG91dHB1dC5uYW1lID0gJ3RoZXRhJykKbW9kLnJhZC5yYW5kbyA9IGZpbGwuc2FtcGxlLm1vZChHUGFyLmRhdGEgPSBkYXRhLnJhbmRvLCBpbnB1dC5uYW1lID0gYygneDEnLCAneDInKSwgb3V0cHV0Lm5hbWUgPSAncmFkJykKbW9kLmFuZy5yYW5kbyA9IGZpbGwuc2FtcGxlLm1vZChHUGFyLmRhdGEgPSBkYXRhLnJhbmRvLCBpbnB1dC5uYW1lID0gYygneDEnLCAneDInKSwgb3V0cHV0Lm5hbWUgPSAndGhldGEnKQoKcmVzMSA9IHByZWRpY3Qob2JqZWN0ID0gbW9kLnJhZC5wYXJldCwgbmV3ZGF0YSA9IGRhdGEuZnJhbWUoeDEgPSBmaW5lLmdyaWQkeDEsIHgyID0gZmluZS5ncmlkJHgyKSwgdHlwZSA9ICJVSyIpCnJlczIgPSBwcmVkaWN0KG9iamVjdCA9IG1vZC5hbmcucGFyZXQsIG5ld2RhdGEgPSBkYXRhLmZyYW1lKHgxID0gZmluZS5ncmlkJHgxLCB4MiA9IGZpbmUuZ3JpZCR4MiksIHR5cGUgPSAiVUsiKQpmaW5lLmdyaWQkcHJvYi5yYWRhbi5wYXJldCA9IHBub3JtKHEgPSAwLCBtZWFuID0gcmVzMSRtZWFuIC0gMSwgc2QgPSByZXMxJHNkKSAqIAogICgxIC0gcG5vcm0ocSA9IDAsIG1lYW4gPSByZXMyJG1lYW4gLSAyMCwgc2QgPSByZXMyJHNkKSkKcmVzMSA9IHByZWRpY3Qob2JqZWN0ID0gbW9kLnJhZC5hZGFwdCwgbmV3ZGF0YSA9IGRhdGEuZnJhbWUoeDEgPSBmaW5lLmdyaWQkeDEsIHgyID0gZmluZS5ncmlkJHgyKSwgdHlwZSA9ICJVSyIpCnJlczIgPSBwcmVkaWN0KG9iamVjdCA9IG1vZC5hbmcuYWRhcHQsIG5ld2RhdGEgPSBkYXRhLmZyYW1lKHgxID0gZmluZS5ncmlkJHgxLCB4MiA9IGZpbmUuZ3JpZCR4MiksIHR5cGUgPSAiVUsiKQpmaW5lLmdyaWQkcHJvYi5yYWRhbi5hZGFwdCA9IHBub3JtKHEgPSAwLCBtZWFuID0gcmVzMSRtZWFuIC0gMSwgc2QgPSByZXMxJHNkKSAqIAogICgxIC0gcG5vcm0ocSA9IDAsIG1lYW4gPSByZXMyJG1lYW4gLSAyMCwgc2QgPSByZXMyJHNkKSkKcmVzMSA9IHByZWRpY3Qob2JqZWN0ID0gbW9kLnJhZC5yYW5kbywgbmV3ZGF0YSA9IGRhdGEuZnJhbWUoeDEgPSBmaW5lLmdyaWQkeDEsIHgyID0gZmluZS5ncmlkJHgyKSwgdHlwZSA9ICJVSyIpCnJlczIgPSBwcmVkaWN0KG9iamVjdCA9IG1vZC5hbmcucmFuZG8sIG5ld2RhdGEgPSBkYXRhLmZyYW1lKHgxID0gZmluZS5ncmlkJHgxLCB4MiA9IGZpbmUuZ3JpZCR4MiksIHR5cGUgPSAiVUsiKQpmaW5lLmdyaWQkcHJvYi5yYWRhbi5yYW5kbyA9IHBub3JtKHEgPSAwLCBtZWFuID0gcmVzMSRtZWFuIC0gMSwgc2QgPSByZXMxJHNkKSAqIAogICgxIC0gcG5vcm0ocSA9IDAsIG1lYW4gPSByZXMyJG1lYW4gLSAyMCwgc2QgPSByZXMyJHNkKSkKCmBgYAoKYGBge3IgR1A6IFBsb3R0aW5nIFRydWUgUmVzdWx0c30Kc2VwID0gMApnZ3Bsb3QoKSArCiAgIyBCb3VuZGFyaWVzOiArLy0gc29tZSBzZXBhcmF0aW9uIGZyb20gMC41CiAgZ2VvbV9jb250b3VyKGRhdGEgPSBmaW5lLmdyaWQsIG1hcHBpbmcgPSBhZXMoeCA9IHgxLCB5ID0geDIsIHogPSBwcm9iLmRlbHRhLnBhcmV0LCBjb2xvciA9ICdkZWx0YScpLCAKICAgICAgICAgICAgICAgYnJlYWtzID0gYyhzZXAsIC1zZXApKzAuNSkgKwogIGdlb21fY29udG91cihkYXRhID0gZmluZS5ncmlkLCBtYXBwaW5nID0gYWVzKHggPSB4MSwgeSA9IHgyLCB6ID0gcHJvYi5jdXRvZi5wYXJldCwgY29sb3IgPSAnY3V0b2YnKSwgCiAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoc2VwLCAtc2VwKSswLjUpICsKICBnZW9tX2NvbnRvdXIoZGF0YSA9IGZpbmUuZ3JpZCwgbWFwcGluZyA9IGFlcyh4ID0geDEsIHkgPSB4MiwgeiA9IHByb2IucmFkYW4ucGFyZXQsIGNvbG9yID0gJ3JhZGFuJyksIAogICAgICAgICAgICAgICBicmVha3MgPSBjKHNlcCwgLXNlcCkrMC41KSArCiAgIyBQYXJldG8gZnJvbnRpZXIKICBnZW9tX3BvaW50KGRhdGEgPSBHUGFyLmZyb250LCBtYXBwaW5nID0gYWVzKHggPSB4MSwgeSA9IHgyKSwgY29sb3IgPSAnYmxhY2snKSArIAogIGdlb21fc21vb3RoKGRhdGEgPSBHUGFyLmZyb250LCBtYXBwaW5nID0gYWVzKHggPSB4MSwgeSA9IHgyLCBjb2xvciA9ICdQYXJldG8nKSwgbGV2ZWwgPSAwLjk1LCBtZXRob2QgPSAnbG9lc3MnKSArIAogIGxhYnMoeCA9IGV4cHJlc3Npb24oJ3gnWzFdKSwgeSA9IGV4cHJlc3Npb24oJ3gnWzJdKSwgY29sb3IgPSAnQWNjZXB0YW5jZSBDcml0ZXJpYScsIHN1YnRpdGxlID0gJ0JlZm9yZSBSZWZpbmVtZW50JykgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCdkZWx0YScgPSAncmVkJywgJ2N1dG9mJyA9ICdza3libHVlMicsICdyYWRhbicgPSAnZ3JlZW4nLCAnUGFyZXRvJyA9ICdibGFjaycpLAogICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCdkZWx0YScgPSBleHByZXNzaW9uKGRlbHRhKicgPCAxJyksICdjdXRvZicgPSBleHByZXNzaW9uKCdGJ1sxXV4nKicqJzwgMSwgRidbMl1eJyonKic8IDEnKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ3JhZGFuJyA9IGV4cHJlc3Npb24oJ3IgPCAxLCAnKnRoZXRhKicgPiAxOCdeJ28nKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnUGFyZXRvJyA9IGV4cHJlc3Npb24oJ1BhcmV0byBGcm9udCcpKSwKICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYygnZGVsdGEnLCAnY3V0b2YnLCAncmFkYW4nLCAnUGFyZXRvJykpICsKICB0aGVtZV9jbGFzc2ljKCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSBjKDAuODUsIDAuNzUpKSArIAogIHNjYWxlX3hfY29udGludW91cyhleHBhbmQgPSBjKDAsIDApLCBsaW1pdHMgPSBjKDAsIDUpKSArIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBjKDAsIDApLCBsaW1pdHMgPSBjKDAsIDUpKSArCiAgZ3VpZGVzKGNvbG91ciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KGZpbGwgPSBhbHBoYSgnd2hpdGUnLCAxKSkpKQoKZ2dwbG90KCkgKwogICMgQm91bmRhcmllczogKy8tIHNvbWUgc2VwYXJhdGlvbiBmcm9tIDAuNQogIGdlb21fY29udG91cihkYXRhID0gZmluZS5ncmlkLCBtYXBwaW5nID0gYWVzKHggPSB4MSwgeSA9IHgyLCB6ID0gcHJvYi5kZWx0YS5hZGFwdCwgY29sb3IgPSAnZGVsdGEnKSwgCiAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoc2VwLCAtc2VwKSswLjUpICsKICBnZW9tX2NvbnRvdXIoZGF0YSA9IGZpbmUuZ3JpZCwgbWFwcGluZyA9IGFlcyh4ID0geDEsIHkgPSB4MiwgeiA9IHByb2IuY3V0b2YuYWRhcHQsIGNvbG9yID0gJ2N1dG9mJyksIAogICAgICAgICAgICAgICBicmVha3MgPSBjKHNlcCwgLXNlcCkrMC41KSArCiAgZ2VvbV9jb250b3VyKGRhdGEgPSBmaW5lLmdyaWQsIG1hcHBpbmcgPSBhZXMoeCA9IHgxLCB5ID0geDIsIHogPSBwcm9iLnJhZGFuLmFkYXB0LCBjb2xvciA9ICdyYWRhbicpLCAKICAgICAgICAgICAgICAgYnJlYWtzID0gYyhzZXAsIC1zZXApKzAuNSkgKwogICMgUGFyZXRvIGZyb250aWVyCiAgZ2VvbV9wb2ludChkYXRhID0gR1Bhci5mcm9udCwgbWFwcGluZyA9IGFlcyh4ID0geDEsIHkgPSB4MiksIGNvbG9yID0gJ2JsYWNrJykgKyAKICBnZW9tX3Ntb290aChkYXRhID0gR1Bhci5mcm9udCwgbWFwcGluZyA9IGFlcyh4ID0geDEsIHkgPSB4MiwgY29sb3IgPSAnUGFyZXRvJyksIGxldmVsID0gMC45NSwgbWV0aG9kID0gJ2xvZXNzJykgKyAKICBsYWJzKHggPSBleHByZXNzaW9uKCd4J1sxXSksIHkgPSBleHByZXNzaW9uKCd4J1syXSksIGNvbG9yID0gJ0FjY2VwdGFuY2UgQ3JpdGVyaWEnLCBzdWJ0aXRsZSA9ICdBZnRlciBBZGFwdGl2ZSBTYW1wbGluZyBSZWZpbmVtZW50JykgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCdkZWx0YScgPSAncmVkJywgJ2N1dG9mJyA9ICdza3libHVlMicsICdyYWRhbicgPSAnZ3JlZW4nLCAnUGFyZXRvJyA9ICdibGFjaycpLAogICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCdkZWx0YScgPSBleHByZXNzaW9uKGRlbHRhKicgPCAxJyksICdjdXRvZicgPSBleHByZXNzaW9uKCdGJ1sxXV4nKicqJzwgMSwgRidbMl1eJyonKic8IDEnKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ3JhZGFuJyA9IGV4cHJlc3Npb24oJ3IgPCAxLCAnKnRoZXRhKicgPiAxOCdeJ28nKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnUGFyZXRvJyA9IGV4cHJlc3Npb24oJ1BhcmV0byBGcm9udCcpKSwKICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYygnZGVsdGEnLCAnY3V0b2YnLCAncmFkYW4nLCAnUGFyZXRvJykpICsKICB0aGVtZV9jbGFzc2ljKCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSBjKDAuODUsIDAuNzUpKSArIAogIHNjYWxlX3hfY29udGludW91cyhleHBhbmQgPSBjKDAsIDApLCBsaW1pdHMgPSBjKDAsIDUpKSArIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBjKDAsIDApLCBsaW1pdHMgPSBjKDAsIDUpKSArCiAgZ3VpZGVzKGNvbG91ciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KGZpbGwgPSBhbHBoYSgnd2hpdGUnLCAxKSkpKQoKZ2dwbG90KCkgKwogICMgQm91bmRhcmllczogKy8tIHNvbWUgc2VwYXJhdGlvbiBmcm9tIDAuNQogIGdlb21fY29udG91cihkYXRhID0gZmluZS5ncmlkLCBtYXBwaW5nID0gYWVzKHggPSB4MSwgeSA9IHgyLCB6ID0gcHJvYi5kZWx0YS5yYW5kbywgY29sb3IgPSAnZGVsdGEnKSwgCiAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoc2VwLCAtc2VwKSswLjUpICsKICBnZW9tX2NvbnRvdXIoZGF0YSA9IGZpbmUuZ3JpZCwgbWFwcGluZyA9IGFlcyh4ID0geDEsIHkgPSB4MiwgeiA9IHByb2IuY3V0b2YucmFuZG8sIGNvbG9yID0gJ2N1dG9mJyksIAogICAgICAgICAgICAgICBicmVha3MgPSBjKHNlcCwgLXNlcCkrMC41KSArCiAgZ2VvbV9jb250b3VyKGRhdGEgPSBmaW5lLmdyaWQsIG1hcHBpbmcgPSBhZXMoeCA9IHgxLCB5ID0geDIsIHogPSBwcm9iLnJhZGFuLnJhbmRvLCBjb2xvciA9ICdyYWRhbicpLCAKICAgICAgICAgICAgICAgYnJlYWtzID0gYyhzZXAsIC1zZXApKzAuNSkgKwogICMgUGFyZXRvIGZyb250aWVyCiAgZ2VvbV9wb2ludChkYXRhID0gR1Bhci5mcm9udCwgbWFwcGluZyA9IGFlcyh4ID0geDEsIHkgPSB4MiksIGNvbG9yID0gJ2JsYWNrJykgKyAKICBnZW9tX3Ntb290aChkYXRhID0gR1Bhci5mcm9udCwgbWFwcGluZyA9IGFlcyh4ID0geDEsIHkgPSB4MiwgY29sb3IgPSAnUGFyZXRvJyksIGxldmVsID0gMC45NSwgbWV0aG9kID0gJ2xvZXNzJykgKyAKICBsYWJzKHggPSBleHByZXNzaW9uKCd4J1sxXSksIHkgPSBleHByZXNzaW9uKCd4J1syXSksIGNvbG9yID0gJ0FjY2VwdGFuY2UgQ3JpdGVyaWEnLCBzdWJ0aXRsZSA9ICdBZnRlciBSYW5kb20gU2FtcGxpbmcnKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoJ2RlbHRhJyA9ICdyZWQnLCAnY3V0b2YnID0gJ3NreWJsdWUyJywgJ3JhZGFuJyA9ICdncmVlbicsICdQYXJldG8nID0gJ2JsYWNrJyksCiAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoJ2RlbHRhJyA9IGV4cHJlc3Npb24oZGVsdGEqJyA8IDEnKSwgJ2N1dG9mJyA9IGV4cHJlc3Npb24oJ0YnWzFdXicqJyonPCAxLCBGJ1syXV4nKicqJzwgMScpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAncmFkYW4nID0gZXhwcmVzc2lvbignciA8IDEsICcqdGhldGEqJyA+IDE4J14nbycpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdQYXJldG8nID0gZXhwcmVzc2lvbignUGFyZXRvIEZyb250JykpLAogICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKCdkZWx0YScsICdjdXRvZicsICdyYWRhbicsICdQYXJldG8nKSkgKwogIHRoZW1lX2NsYXNzaWMoKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC44NSwgMC43NSkpICsgCiAgc2NhbGVfeF9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCksIGxpbWl0cyA9IGMoMCwgNSkpICsgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCksIGxpbWl0cyA9IGMoMCwgNSkpICsKICBndWlkZXMoY29sb3VyID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3QoZmlsbCA9IGFscGhhKCd3aGl0ZScsIDEpKSkpCgpgYGAKCmBgYHtyIEdQOiBDb21wYXJpc29uIG9mIHNldHMgdG8gdGhlIHJlYWwgcmVzdWx0fQojIFBsb3R0aW5nIHZhcmlhYmxlcwpzZXAgPSAwLjA1OyB0cmFucyA9IDAuMjU7IGxuLnN6ID0gMS4yNQoKIyMgRGlzdGFuY2UKZ2dwbG90KCkgKwogICMgQm91bmRhcmllczogKy8tIHNvbWUgc2VwYXJhdGlvbiBmcm9tIDAuNQogIGdlb21fY29udG91cl9maWxsZWQoZGF0YSA9IGZpbmUuZ3JpZCwgbWFwcGluZyA9IGFlcyh4ID0geDEsIHkgPSB4MiwgeiA9IHByb2IuZGVsdGEucGFyZXQsIAogICAgICAgICAgICAgICBmaWxsID0gJ3BhcmV0JyksIAogICAgICAgICAgICAgICBicmVha3MgPSBjKHNlcCwgLXNlcCkrMC41LCBsaW5ldHlwZSA9IDIsIGFscGhhID0gdHJhbnMpICsKICBnZW9tX2NvbnRvdXJfZmlsbGVkKGRhdGEgPSBmaW5lLmdyaWQsIG1hcHBpbmcgPSBhZXMoeCA9IHgxLCB5ID0geDIsIHogPSBwcm9iLmRlbHRhLmFkYXB0LCAKICAgICAgICAgICAgICAgZmlsbCA9ICdhZGFwdCcpLCAKICAgICAgICAgICAgICAgYnJlYWtzID0gYyhzZXAsIC1zZXApKzAuNSwgbGluZXR5cGUgPSAyLCBhbHBoYSA9IHRyYW5zKSArCiAgZ2VvbV9jb250b3VyX2ZpbGxlZChkYXRhID0gZmluZS5ncmlkLCBtYXBwaW5nID0gYWVzKHggPSB4MSwgeSA9IHgyLCB6ID0gcHJvYi5kZWx0YS5yYW5kbywgCiAgICAgICAgICAgICAgIGZpbGwgPSAncmFuZG8nKSwgCiAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoc2VwLCAtc2VwKSswLjUsIGxpbmV0eXBlID0gMiwgYWxwaGEgPSB0cmFucykgKwogICMgQWN0dWFsIGJvdW5kYXJpZXMKICBnZW9tX2NvbnRvdXIoZGF0YSA9IGZpbmUuZ3JpZCwgbWFwcGluZyA9IGFlcyh4ID0geDEsIHkgPSB4MiwgeiA9IHByb2IuZGVsdGEucGFyZXQsIGNvbG9yID0gJ3BhcmV0JyksIAogICAgICAgICAgICAgICBicmVha3MgPSAwLjUsIGxpbmV0eXBlID0gMSwgc2l6ZSA9IGxuLnN6KSArCiAgZ2VvbV9jb250b3VyKGRhdGEgPSBmaW5lLmdyaWQsIG1hcHBpbmcgPSBhZXMoeCA9IHgxLCB5ID0geDIsIHogPSBwcm9iLmRlbHRhLmFkYXB0LCBjb2xvciA9ICdhZGFwdCcpLCAKICAgICAgICAgICAgICAgYnJlYWtzID0gMC41LCBsaW5ldHlwZSA9IDEsIHNpemUgPSBsbi5zeikgKwogIGdlb21fY29udG91cihkYXRhID0gZmluZS5ncmlkLCBtYXBwaW5nID0gYWVzKHggPSB4MSwgeSA9IHgyLCB6ID0gcHJvYi5kZWx0YS5yYW5kbywgY29sb3IgPSAncmFuZG8nKSwgCiAgICAgICAgICAgICAgIGJyZWFrcyA9IDAuNSwgbGluZXR5cGUgPSAxLCBzaXplID0gbG4uc3opICsKICBnZW9tX2NvbnRvdXIoZGF0YSA9IGZpbmUuZ3JpZCwgbWFwcGluZyA9IGFlcyh4ID0geDEsIHkgPSB4MiwgeiA9IGRlbHRhLCBjb2xvciA9ICd0cnUnKSwgCiAgICAgICAgICAgICAgIGJyZWFrcyA9IDAuNSwgbGluZXR5cGUgPSAxLCBzaXplID0gbG4uc3opICsKICBzY2FsZV9jb2xvcl9tYW51YWwoYnJlYWtzID0gYygndHJ1JywgJ3BhcmV0JywgJ2FkYXB0JywgJ3JhbmRvJyksCiAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoJ3RydScgPSAnVHJ1ZSBCb3VuZGFyeScsICdwYXJldCcgPSAnU3RhcnRpbmcgRGF0YXNldCcsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdhZGFwdCcgPSAnKyBBZGFwdGl2ZSBTYW1wbGluZycsICdyYW5kbycgPSAnKyBSYW5kb20gU2FtcGxpbmcnKSwKICAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gYygndHJ1JyA9ICdibGFjaycsICdwYXJldCcgPSAnc2t5Ymx1ZTInLCAnYWRhcHQnID0gJ3JlZCcsICdyYW5kbycgPSAnZ3JlZW4nKSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKGJyZWFrcyA9IGMoJ3RydScsICdwYXJldCcsICdhZGFwdCcsICdyYW5kbycpLAogICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCd0cnUnID0gJ1RydWUgQm91bmRhcnknLCAncGFyZXQnID0gJ1N0YXJ0aW5nIERhdGFzZXQnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnYWRhcHQnID0gJysgQWRhcHRpdmUgU2FtcGxpbmcnLCAncmFuZG8nID0gJysgUmFuZG9tIFNhbXBsaW5nJyksCiAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGMoJ3RydScgPSAnYmxhY2snLCAncGFyZXQnID0gJ3NreWJsdWUyJywgJ2FkYXB0JyA9ICdyZWQnLCAncmFuZG8nID0gJ2dyZWVuJykpICsKICBsYWJzKHggPSBleHByZXNzaW9uKCd4J1sxXSksIHggPSBleHByZXNzaW9uKCd4J1syXSksIHN1YnRpdGxlID0gJ0NyaXRlcmlhOiBQYXJldG8gRGlzdGFuY2UnLCBjb2xvciA9ICcnKSArCiAgdGhlbWVfY2xhc3NpYygpICsgZ3VpZGVzKGZpbGwgPSBGQUxTRSkgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSBjKDAuODUsIDAuODUpKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwgNSksIGV4cGFuZCA9IGMoMCwgMCkpICsgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwgNSksIGV4cGFuZCA9IGMoMCwgMCkpCgojIyBUaHJlc2hvbGQKZ2dwbG90KCkgKwogICMgQm91bmRhcmllczogKy8tIHNvbWUgc2VwYXJhdGlvbiBmcm9tIDAuNQogIGdlb21fY29udG91cl9maWxsZWQoZGF0YSA9IGZpbmUuZ3JpZCwgbWFwcGluZyA9IGFlcyh4ID0geDEsIHkgPSB4MiwgeiA9IHByb2IuY3V0b2YucGFyZXQsIAogICAgICAgICAgICAgICBmaWxsID0gJ3BhcmV0JyksIAogICAgICAgICAgICAgICBicmVha3MgPSBjKHNlcCwgLXNlcCkrMC41LCBsaW5ldHlwZSA9IDIsIGFscGhhID0gdHJhbnMpICsKICBnZW9tX2NvbnRvdXJfZmlsbGVkKGRhdGEgPSBmaW5lLmdyaWQsIG1hcHBpbmcgPSBhZXMoeCA9IHgxLCB5ID0geDIsIHogPSBwcm9iLmN1dG9mLmFkYXB0LCAKICAgICAgICAgICAgICAgZmlsbCA9ICdhZGFwdCcpLCAKICAgICAgICAgICAgICAgYnJlYWtzID0gYyhzZXAsIC1zZXApKzAuNSwgbGluZXR5cGUgPSAyLCBhbHBoYSA9IHRyYW5zKSArCiAgZ2VvbV9jb250b3VyX2ZpbGxlZChkYXRhID0gZmluZS5ncmlkLCBtYXBwaW5nID0gYWVzKHggPSB4MSwgeSA9IHgyLCB6ID0gcHJvYi5jdXRvZi5yYW5kbywgCiAgICAgICAgICAgICAgIGZpbGwgPSAncmFuZG8nKSwgCiAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoc2VwLCAtc2VwKSswLjUsIGxpbmV0eXBlID0gMiwgYWxwaGEgPSB0cmFucykgKwogICMgQWN0dWFsIGJvdW5kYXJpZXMKICBnZW9tX2NvbnRvdXIoZGF0YSA9IGZpbmUuZ3JpZCwgbWFwcGluZyA9IGFlcyh4ID0geDEsIHkgPSB4MiwgeiA9IHByb2IuY3V0b2YucGFyZXQsIGNvbG9yID0gJ3BhcmV0JyksIAogICAgICAgICAgICAgICBicmVha3MgPSAwLjUsIGxpbmV0eXBlID0gMSwgc2l6ZSA9IGxuLnN6KSArCiAgZ2VvbV9jb250b3VyKGRhdGEgPSBmaW5lLmdyaWQsIG1hcHBpbmcgPSBhZXMoeCA9IHgxLCB5ID0geDIsIHogPSBwcm9iLmN1dG9mLmFkYXB0LCBjb2xvciA9ICdhZGFwdCcpLCAKICAgICAgICAgICAgICAgYnJlYWtzID0gMC41LCBsaW5ldHlwZSA9IDEsIHNpemUgPSBsbi5zeikgKwogIGdlb21fY29udG91cihkYXRhID0gZmluZS5ncmlkLCBtYXBwaW5nID0gYWVzKHggPSB4MSwgeSA9IHgyLCB6ID0gcHJvYi5jdXRvZi5yYW5kbywgY29sb3IgPSAncmFuZG8nKSwgCiAgICAgICAgICAgICAgIGJyZWFrcyA9IDAuNSwgbGluZXR5cGUgPSAxLCBzaXplID0gbG4uc3opICsKICBnZW9tX2NvbnRvdXIoZGF0YSA9IGZpbmUuZ3JpZCwgbWFwcGluZyA9IGFlcyh4ID0geDEsIHkgPSB4MiwgeiA9IGN1dG9mLCBjb2xvciA9ICd0cnUnKSwgCiAgICAgICAgICAgICAgIGJyZWFrcyA9IDAuNSwgbGluZXR5cGUgPSAxLCBzaXplID0gbG4uc3opICsKICBzY2FsZV9jb2xvcl9tYW51YWwoYnJlYWtzID0gYygndHJ1JywgJ3BhcmV0JywgJ2FkYXB0JywgJ3JhbmRvJyksCiAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoJ3RydScgPSAnVHJ1ZSBCb3VuZGFyeScsICdwYXJldCcgPSAnU3RhcnRpbmcgRGF0YXNldCcsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdhZGFwdCcgPSAnKyBBZGFwdGl2ZSBTYW1wbGluZycsICdyYW5kbycgPSAnKyBSYW5kb20gU2FtcGxpbmcnKSwKICAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gYygndHJ1JyA9ICdibGFjaycsICdwYXJldCcgPSAnc2t5Ymx1ZTInLCAnYWRhcHQnID0gJ3JlZCcsICdyYW5kbycgPSAnZ3JlZW4nKSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKGJyZWFrcyA9IGMoJ3RydScsICdwYXJldCcsICdhZGFwdCcsICdyYW5kbycpLAogICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCd0cnUnID0gJ1RydWUgQm91bmRhcnknLCAncGFyZXQnID0gJ1N0YXJ0aW5nIERhdGFzZXQnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnYWRhcHQnID0gJysgQWRhcHRpdmUgU2FtcGxpbmcnLCAncmFuZG8nID0gJysgUmFuZG9tIFNhbXBsaW5nJyksCiAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGMoJ3RydScgPSAnYmxhY2snLCAncGFyZXQnID0gJ3NreWJsdWUyJywgJ2FkYXB0JyA9ICdyZWQnLCAncmFuZG8nID0gJ2dyZWVuJykpICsKICBsYWJzKHggPSBleHByZXNzaW9uKCd4J1sxXSksIHggPSBleHByZXNzaW9uKCd4J1syXSksIHN1YnRpdGxlID0gJ0NyaXRlcmlhOiBPYmplY3RpdmUgZnVuY3Rpb24gdmFsdWVzJywgY29sb3IgPSAnJykgKwogIHRoZW1lX2NsYXNzaWMoKSArIGd1aWRlcyhmaWxsID0gRkFMU0UpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gYygwLjg1LCAwLjg1KSkgKwogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDUpLCBleHBhbmQgPSBjKDAsIDApKSArIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDUpLCBleHBhbmQgPSBjKDAsIDApKQoKIyMgUmFkaXVzLWFuZ2xlCmdncGxvdCgpICsKICAjIEJvdW5kYXJpZXM6ICsvLSBzb21lIHNlcGFyYXRpb24gZnJvbSAwLjUKICBnZW9tX2NvbnRvdXJfZmlsbGVkKGRhdGEgPSBmaW5lLmdyaWQsIG1hcHBpbmcgPSBhZXMoeCA9IHgxLCB5ID0geDIsIHogPSBwcm9iLnJhZGFuLnBhcmV0LCAKICAgICAgICAgICAgICAgZmlsbCA9ICdwYXJldCcpLCAKICAgICAgICAgICAgICAgYnJlYWtzID0gYyhzZXAsIC1zZXApKzAuNSwgbGluZXR5cGUgPSAyLCBhbHBoYSA9IHRyYW5zKSArCiAgZ2VvbV9jb250b3VyX2ZpbGxlZChkYXRhID0gZmluZS5ncmlkLCBtYXBwaW5nID0gYWVzKHggPSB4MSwgeSA9IHgyLCB6ID0gcHJvYi5yYWRhbi5hZGFwdCwgCiAgICAgICAgICAgICAgIGZpbGwgPSAnYWRhcHQnKSwgCiAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoc2VwLCAtc2VwKSswLjUsIGxpbmV0eXBlID0gMiwgYWxwaGEgPSB0cmFucykgKwogIGdlb21fY29udG91cl9maWxsZWQoZGF0YSA9IGZpbmUuZ3JpZCwgbWFwcGluZyA9IGFlcyh4ID0geDEsIHkgPSB4MiwgeiA9IHByb2IucmFkYW4ucmFuZG8sIAogICAgICAgICAgICAgICBmaWxsID0gJ3JhbmRvJyksIAogICAgICAgICAgICAgICBicmVha3MgPSBjKHNlcCwgLXNlcCkrMC41LCBsaW5ldHlwZSA9IDIsIGFscGhhID0gdHJhbnMpICsKICAjIEFjdHVhbCBib3VuZGFyaWVzCiAgZ2VvbV9jb250b3VyKGRhdGEgPSBmaW5lLmdyaWQsIG1hcHBpbmcgPSBhZXMoeCA9IHgxLCB5ID0geDIsIHogPSBwcm9iLnJhZGFuLnBhcmV0LCBjb2xvciA9ICdwYXJldCcpLCAKICAgICAgICAgICAgICAgYnJlYWtzID0gMC41LCBsaW5ldHlwZSA9IDEsIHNpemUgPSBsbi5zeikgKwogIGdlb21fY29udG91cihkYXRhID0gZmluZS5ncmlkLCBtYXBwaW5nID0gYWVzKHggPSB4MSwgeSA9IHgyLCB6ID0gcHJvYi5yYWRhbi5hZGFwdCwgY29sb3IgPSAnYWRhcHQnKSwgCiAgICAgICAgICAgICAgIGJyZWFrcyA9IDAuNSwgbGluZXR5cGUgPSAxLCBzaXplID0gbG4uc3opICsKICBnZW9tX2NvbnRvdXIoZGF0YSA9IGZpbmUuZ3JpZCwgbWFwcGluZyA9IGFlcyh4ID0geDEsIHkgPSB4MiwgeiA9IHByb2IucmFkYW4ucmFuZG8sIGNvbG9yID0gJ3JhbmRvJyksIAogICAgICAgICAgICAgICBicmVha3MgPSAwLjUsIGxpbmV0eXBlID0gMSwgc2l6ZSA9IGxuLnN6KSArCiAgZ2VvbV9jb250b3VyKGRhdGEgPSBmaW5lLmdyaWQsIG1hcHBpbmcgPSBhZXMoeCA9IHgxLCB5ID0geDIsIHogPSByYWRhbiwgY29sb3IgPSAndHJ1JyksIAogICAgICAgICAgICAgICBicmVha3MgPSAwLjUsIGxpbmV0eXBlID0gMSwgc2l6ZSA9IGxuLnN6KSArCiAgc2NhbGVfY29sb3JfbWFudWFsKGJyZWFrcyA9IGMoJ3RydScsICdwYXJldCcsICdhZGFwdCcsICdyYW5kbycpLAogICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCd0cnUnID0gJ1RydWUgQm91bmRhcnknLCAncGFyZXQnID0gJ1N0YXJ0aW5nIERhdGFzZXQnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnYWRhcHQnID0gJysgQWRhcHRpdmUgU2FtcGxpbmcnLCAncmFuZG8nID0gJysgUmFuZG9tIFNhbXBsaW5nJyksCiAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGMoJ3RydScgPSAnYmxhY2snLCAncGFyZXQnID0gJ3NreWJsdWUyJywgJ2FkYXB0JyA9ICdyZWQnLCAncmFuZG8nID0gJ2dyZWVuJykpICsKICBzY2FsZV9maWxsX21hbnVhbChicmVha3MgPSBjKCd0cnUnLCAncGFyZXQnLCAnYWRhcHQnLCAncmFuZG8nKSwKICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygndHJ1JyA9ICdUcnVlIEJvdW5kYXJ5JywgJ3BhcmV0JyA9ICdTdGFydGluZyBEYXRhc2V0JywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2FkYXB0JyA9ICcrIEFkYXB0aXZlIFNhbXBsaW5nJywgJ3JhbmRvJyA9ICcrIFJhbmRvbSBTYW1wbGluZycpLAogICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBjKCd0cnUnID0gJ2JsYWNrJywgJ3BhcmV0JyA9ICdza3libHVlMicsICdhZGFwdCcgPSAncmVkJywgJ3JhbmRvJyA9ICdncmVlbicpKSArCiAgbGFicyh4ID0gZXhwcmVzc2lvbigneCdbMV0pLCB4ID0gZXhwcmVzc2lvbigneCdbMl0pLCBjb2xvciA9ICcnLAogICAgICAgc3VidGl0bGUgPSBleHByZXNzaW9uKCdDcml0ZXJpYTogVXRvcGlhIHBvaW50IGRpc3RhbmNlIGFuZCBmJ1sxXSonIFByaW9yaXR5ID4gODAlJykpICsKICB0aGVtZV9jbGFzc2ljKCkgKyBndWlkZXMoZmlsbCA9IEZBTFNFKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC44NSwgMC44NSkpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCA1KSwgZXhwYW5kID0gYygwLCAwKSkgKyBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCA1KSwgZXhwYW5kID0gYygwLCAwKSkKCgpgYGAKCgpUaGUgZXJyb3IgcmF0ZSBpbiB0aGUgR1AgbW9kZWwgZXN0aW1hdGUgc2hvdWxkIGFjY291bnQgZm9yIHRoZSBidWlsdC1pbiB1bmNlcnRhaW50eSBpbiB0aGUgbW9kZWwuClRoZSBwcm9iYWJpbGl0eSB0aGF0IHRoZSBtb2RlbCBnaXZlcyB0aGUgd3JvbmcgcmVzdWx0IGdpdmVuIHRoZSAkKHhfMSwgeF8yKSQgY29vcmRpbmF0ZXMgaXMgdGhlIHByb2JhYmlsaXR5IHRoYXQgaXQgYWNjZXB0cyB0aGUgcG9pbnQgd2hlbiBpdCBzaG91bGQgcmVqZWN0IGl0LCBvciB2aWNlIHZlcnNhLgpTaW5jZSBhbGwgJCh4XzEsIHhfMikkIGFyZSBlcXVhbGx5IGxpa2VseSBpbiB0aGlzIG1hdGhlbWF0aWNhbCBmdW5jdGlvbiwgdGhlIGVycm9yIHJhdGUgb2YgdGhlIEdQIG1vZGVsIGlzIHRoZSBhdmVyYWdlIG9mIHRoZXNlIHByb2JhYmlsaXRpZXMuClRoZXJlIHNob3VsZCBiZSBhbiBldmlkZW50IGRlY3JlYXNlIGluIHRoZSBlcnJvciByYXRlIGJldHdlZW4gdGhlIHByZS0gYW5kIHBvc3QtcmVmaW5lbWVudCBtb2RlbHMuCgpUaGUgTW9udGUgQ2FybG8gdW5jZXJ0YWludHkgaW4gdGhlIGVycm9yIHJhdGUgaXMgJFxzcXJ0e1xmcmFje3AoMS1wKX17Tn19JC4KClNpbmNlIHRoZSBmaW5lIGdyaWQgY2FsY3VsYXRpb24gd2FzIGFscmVhZHkgY2FsY3VsYXRlZCwgdXNpbmcgdGhhdCBpbnN0ZWFkIG9mIGEgZnVsbHkgcmFuZG9tIGFzc2lnbm1lbnQuCgpBbHNvIGluY2x1ZGUgdGhlIHJhdGUgb2YgdHlwZSBJIChQW3Nob3VsZCByZWplY3QgfCBhY2NlcHRlZF0pIGFuZCB0eXBlIElJIGVycm9yIChQW3Nob3VsZCBhY2NlcHQgfCByZWplY3RlZF0pIGVycm9yIHJhdGVzLgoKQnkgQmF5ZXMgcnVsZSwKJFBbWHxZXSA9IFBbWCwgWV0gUFtYXSAvIFBbWV0kCgpgYGB7ciBHUDogRXJyb3IgcmF0ZX0KZXJyb3IucmF0ZSA9IGRhdGEuZnJhbWUobWV0aG9kID0gJ0dQJywgc291cmNlID0gcmVwKGMoJzBwYXJldCcsICcxYWRhcHQnLCAnMnJhbmRvJyksIDMpLAogICAgICAgICAgICAgICAgICAgICAgICBjcml0ZXJpYSA9IGMocmVwKCdQYXJldG8gRGlzdGFuY2UnLCAzKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXAoJ09iamVjdGl2ZSBDdXRvZmYnLCAzKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXAoJ1V0b3BpYSBEaXN0YW5jZSArIFByaW9yaXR5JywgMykpKQplcnJvci5yYXRlJGNyaXRlcmlhID0gZmFjdG9yKGVycm9yLnJhdGUkY3JpdGVyaWEsIGxldmVscyA9IGMoIlBhcmV0byBEaXN0YW5jZSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk9iamVjdGl2ZSBDdXRvZmYiLCAiVXRvcGlhIERpc3RhbmNlICsgUHJpb3JpdHkiKSkKIyBUb3RhbCBlcnJvciByYXRlCmVycm9yLnJhdGUkcmF0ZSA9CiAgYyhtZWFuKGMoMSAtIGZpbHRlcihmaW5lLmdyaWQsIGRlbHRhID09IDEpJHByb2IuZGVsdGEucGFyZXQsIGZpbHRlcihmaW5lLmdyaWQsIGRlbHRhID09IDApJHByb2IuZGVsdGEucGFyZXQpKSwKICAgIG1lYW4oYygxIC0gZmlsdGVyKGZpbmUuZ3JpZCwgZGVsdGEgPT0gMSkkcHJvYi5kZWx0YS5hZGFwdCwgZmlsdGVyKGZpbmUuZ3JpZCwgZGVsdGEgPT0gMCkkcHJvYi5kZWx0YS5hZGFwdCkpLAogICAgbWVhbihjKDEgLSBmaWx0ZXIoZmluZS5ncmlkLCBkZWx0YSA9PSAxKSRwcm9iLmRlbHRhLnJhbmRvLCBmaWx0ZXIoZmluZS5ncmlkLCBkZWx0YSA9PSAwKSRwcm9iLmRlbHRhLnJhbmRvKSksCiAgICBtZWFuKGMoMSAtIGZpbHRlcihmaW5lLmdyaWQsIGN1dG9mID09IDEpJHByb2IuY3V0b2YucGFyZXQsIGZpbHRlcihmaW5lLmdyaWQsIGN1dG9mID09IDApJHByb2IuY3V0b2YucGFyZXQpKSwKICAgIG1lYW4oYygxIC0gZmlsdGVyKGZpbmUuZ3JpZCwgY3V0b2YgPT0gMSkkcHJvYi5jdXRvZi5hZGFwdCwgZmlsdGVyKGZpbmUuZ3JpZCwgY3V0b2YgPT0gMCkkcHJvYi5jdXRvZi5hZGFwdCkpLAogICAgbWVhbihjKDEgLSBmaWx0ZXIoZmluZS5ncmlkLCBjdXRvZiA9PSAxKSRwcm9iLmN1dG9mLnJhbmRvLCBmaWx0ZXIoZmluZS5ncmlkLCBjdXRvZiA9PSAwKSRwcm9iLmN1dG9mLnJhbmRvKSksCiAgICBtZWFuKGMoMSAtIGZpbHRlcihmaW5lLmdyaWQsIHJhZGFuID09IDEpJHByb2IucmFkYW4ucGFyZXQsIGZpbHRlcihmaW5lLmdyaWQsIHJhZGFuID09IDApJHByb2IucmFkYW4ucGFyZXQpKSwKICAgIG1lYW4oYygxIC0gZmlsdGVyKGZpbmUuZ3JpZCwgcmFkYW4gPT0gMSkkcHJvYi5yYWRhbi5hZGFwdCwgZmlsdGVyKGZpbmUuZ3JpZCwgcmFkYW4gPT0gMCkkcHJvYi5yYWRhbi5hZGFwdCkpLAogICAgbWVhbihjKDEgLSBmaWx0ZXIoZmluZS5ncmlkLCByYWRhbiA9PSAxKSRwcm9iLnJhZGFuLnJhbmRvLCBmaWx0ZXIoZmluZS5ncmlkLCByYWRhbiA9PSAwKSRwcm9iLnJhZGFuLnJhbmRvKSkpCmVycm9yLnJhdGUkZXJyID0gc3FydChlcnJvci5yYXRlJHJhdGUqKDEtZXJyb3IucmF0ZSRyYXRlKS9ucm93KGZpbmUuZ3JpZCkpCgojIFR5cGUgMiBlcnJvciA9IFBbc2hvdWxkIGFjY2VwdCB8IHJlamVjdGVkXQplcnJvci5yYXRlJHR5cDIgPQogIGMoc3VtKDEgLSBmaWx0ZXIoZmluZS5ncmlkLCBkZWx0YSA9PSAxKSRwcm9iLmRlbHRhLnBhcmV0KSpucm93KGZpbHRlcihmaW5lLmdyaWQsIGRlbHRhID09IDEpKS9ucm93KGZpbmUuZ3JpZCleMi8KICAgICAgbWVhbigxIC0gZmluZS5ncmlkJHByb2IuZGVsdGEucGFyZXQpLAogICAgc3VtKDEgLSBmaWx0ZXIoZmluZS5ncmlkLCBkZWx0YSA9PSAxKSRwcm9iLmRlbHRhLmFkYXB0KSpucm93KGZpbHRlcihmaW5lLmdyaWQsIGRlbHRhID09IDEpKS9ucm93KGZpbmUuZ3JpZCleMi8KICAgICAgbWVhbigxIC0gZmluZS5ncmlkJHByb2IuZGVsdGEuYWRhcHQpLAogICAgc3VtKDEgLSBmaWx0ZXIoZmluZS5ncmlkLCBkZWx0YSA9PSAxKSRwcm9iLmRlbHRhLnJhbmRvKSpucm93KGZpbHRlcihmaW5lLmdyaWQsIGRlbHRhID09IDEpKS9ucm93KGZpbmUuZ3JpZCleMi8KICAgICAgbWVhbigxIC0gZmluZS5ncmlkJHByb2IuZGVsdGEucmFuZG8pLAogICAgc3VtKDEgLSBmaWx0ZXIoZmluZS5ncmlkLCBjdXRvZiA9PSAxKSRwcm9iLmN1dG9mLnBhcmV0KSpucm93KGZpbHRlcihmaW5lLmdyaWQsIGN1dG9mID09IDEpKS9ucm93KGZpbmUuZ3JpZCleMi8KICAgICAgbWVhbigxIC0gZmluZS5ncmlkJHByb2IuY3V0b2YucGFyZXQpLAogICAgc3VtKDEgLSBmaWx0ZXIoZmluZS5ncmlkLCBjdXRvZiA9PSAxKSRwcm9iLmN1dG9mLmFkYXB0KSpucm93KGZpbHRlcihmaW5lLmdyaWQsIGN1dG9mID09IDEpKS9ucm93KGZpbmUuZ3JpZCleMi8KICAgICAgbWVhbigxIC0gZmluZS5ncmlkJHByb2IuY3V0b2YuYWRhcHQpLAogICAgc3VtKDEgLSBmaWx0ZXIoZmluZS5ncmlkLCBjdXRvZiA9PSAxKSRwcm9iLmN1dG9mLnJhbmRvKSpucm93KGZpbHRlcihmaW5lLmdyaWQsIGN1dG9mID09IDEpKS9ucm93KGZpbmUuZ3JpZCleMi8KICAgICAgbWVhbigxIC0gZmluZS5ncmlkJHByb2IuY3V0b2YucmFuZG8pLAogICAgc3VtKDEgLSBmaWx0ZXIoZmluZS5ncmlkLCByYWRhbiA9PSAxKSRwcm9iLnJhZGFuLnBhcmV0KSpucm93KGZpbHRlcihmaW5lLmdyaWQsIHJhZGFuID09IDEpKS9ucm93KGZpbmUuZ3JpZCleMi8KICAgICAgbWVhbigxIC0gZmluZS5ncmlkJHByb2IucmFkYW4ucGFyZXQpLAogICAgc3VtKDEgLSBmaWx0ZXIoZmluZS5ncmlkLCByYWRhbiA9PSAxKSRwcm9iLnJhZGFuLmFkYXB0KSpucm93KGZpbHRlcihmaW5lLmdyaWQsIHJhZGFuID09IDEpKS9ucm93KGZpbmUuZ3JpZCleMi8KICAgICAgbWVhbigxIC0gZmluZS5ncmlkJHByb2IucmFkYW4uYWRhcHQpLAogICAgc3VtKDEgLSBmaWx0ZXIoZmluZS5ncmlkLCByYWRhbiA9PSAxKSRwcm9iLnJhZGFuLnJhbmRvKSpucm93KGZpbHRlcihmaW5lLmdyaWQsIHJhZGFuID09IDEpKS9ucm93KGZpbmUuZ3JpZCleMi8KICAgICAgbWVhbigxIC0gZmluZS5ncmlkJHByb2IucmFkYW4ucmFuZG8pKQplcnJvci5yYXRlJHR5cDIuZXJyID0gc3FydChlcnJvci5yYXRlJHR5cDIqKDEtZXJyb3IucmF0ZSR0eXAyKSAvIG5yb3coZmluZS5ncmlkKSkKICAgICAgICAgICAgICAgICAgICAgICAjIGMocmVwKG5yb3coZmlsdGVyKGZpbmUuZ3JpZCwgZGVsdGEgPT0gMCkpLCAzKSwKICAgICAgICAgICAgICAgICAgICAgICAjICAgcmVwKG5yb3coZmlsdGVyKGZpbmUuZ3JpZCwgY3V0b2YgPT0gMCkpLCAzKSwKICAgICAgICAgICAgICAgICAgICAgICAjICAgcmVwKG5yb3coZmlsdGVyKGZpbmUuZ3JpZCwgcmFkYW4gPT0gMCkpLCAzKSkpCgojIFR5cGUgMSBlcnJvciA9IFBbc2hvdWxkIHJlamVjdCB8IGFjY2VwdGVkXQplcnJvci5yYXRlJHR5cDEgPQogIGMoc3VtKGZpbHRlcihmaW5lLmdyaWQsIGRlbHRhID09IDApJHByb2IuZGVsdGEucGFyZXQpKm5yb3coZmlsdGVyKGZpbmUuZ3JpZCwgZGVsdGEgPT0gMCkpL25yb3coZmluZS5ncmlkKV4yLwogICAgICBtZWFuKGZpbmUuZ3JpZCRwcm9iLmRlbHRhLnBhcmV0KSwKICAgIHN1bShmaWx0ZXIoZmluZS5ncmlkLCBkZWx0YSA9PSAwKSRwcm9iLmRlbHRhLmFkYXB0KSpucm93KGZpbHRlcihmaW5lLmdyaWQsIGRlbHRhID09IDApKS9ucm93KGZpbmUuZ3JpZCleMi8KICAgICAgbWVhbihmaW5lLmdyaWQkcHJvYi5kZWx0YS5hZGFwdCksCiAgICBzdW0oZmlsdGVyKGZpbmUuZ3JpZCwgZGVsdGEgPT0gMCkkcHJvYi5kZWx0YS5yYW5kbykqbnJvdyhmaWx0ZXIoZmluZS5ncmlkLCBkZWx0YSA9PSAwKSkvbnJvdyhmaW5lLmdyaWQpXjIvCiAgICAgIG1lYW4oZmluZS5ncmlkJHByb2IuZGVsdGEucmFuZG8pLAogICAgc3VtKGZpbHRlcihmaW5lLmdyaWQsIGN1dG9mID09IDApJHByb2IuY3V0b2YucGFyZXQpKm5yb3coZmlsdGVyKGZpbmUuZ3JpZCwgY3V0b2YgPT0gMCkpL25yb3coZmluZS5ncmlkKV4yLwogICAgICBtZWFuKGZpbmUuZ3JpZCRwcm9iLmN1dG9mLnBhcmV0KSwKICAgIHN1bShmaWx0ZXIoZmluZS5ncmlkLCBjdXRvZiA9PSAwKSRwcm9iLmN1dG9mLmFkYXB0KSpucm93KGZpbHRlcihmaW5lLmdyaWQsIGN1dG9mID09IDApKS9ucm93KGZpbmUuZ3JpZCleMi8KICAgICAgbWVhbihmaW5lLmdyaWQkcHJvYi5jdXRvZi5hZGFwdCksCiAgICBzdW0oZmlsdGVyKGZpbmUuZ3JpZCwgY3V0b2YgPT0gMCkkcHJvYi5jdXRvZi5yYW5kbykqbnJvdyhmaWx0ZXIoZmluZS5ncmlkLCBjdXRvZiA9PSAwKSkvbnJvdyhmaW5lLmdyaWQpXjIvCiAgICAgIG1lYW4oZmluZS5ncmlkJHByb2IuY3V0b2YucmFuZG8pLAogICAgc3VtKGZpbHRlcihmaW5lLmdyaWQsIHJhZGFuID09IDApJHByb2IucmFkYW4ucGFyZXQpKm5yb3coZmlsdGVyKGZpbmUuZ3JpZCwgcmFkYW4gPT0gMCkpL25yb3coZmluZS5ncmlkKV4yLwogICAgICBtZWFuKGZpbmUuZ3JpZCRwcm9iLnJhZGFuLnBhcmV0KSwKICAgIHN1bShmaWx0ZXIoZmluZS5ncmlkLCByYWRhbiA9PSAwKSRwcm9iLnJhZGFuLmFkYXB0KSpucm93KGZpbHRlcihmaW5lLmdyaWQsIHJhZGFuID09IDApKS9ucm93KGZpbmUuZ3JpZCleMi8KICAgICAgbWVhbihmaW5lLmdyaWQkcHJvYi5yYWRhbi5hZGFwdCksCiAgICBzdW0oZmlsdGVyKGZpbmUuZ3JpZCwgcmFkYW4gPT0gMCkkcHJvYi5yYWRhbi5yYW5kbykqbnJvdyhmaWx0ZXIoZmluZS5ncmlkLCByYWRhbiA9PSAwKSkvbnJvdyhmaW5lLmdyaWQpXjIvCiAgICAgIG1lYW4oZmluZS5ncmlkJHByb2IucmFkYW4ucmFuZG8pKQplcnJvci5yYXRlJHR5cDEuZXJyID0gc3FydChlcnJvci5yYXRlJHR5cDEqKDEtZXJyb3IucmF0ZSR0eXAxKSAvIG5yb3coZmluZS5ncmlkKSkKICAgICAgICAgICAgICAgICAgICAgICAjIGMocmVwKG5yb3coZmlsdGVyKGZpbmUuZ3JpZCwgZGVsdGEgPT0gMCkpLCAzKSwKICAgICAgICAgICAgICAgICAgICAgICAjICAgcmVwKG5yb3coZmlsdGVyKGZpbmUuZ3JpZCwgY3V0b2YgPT0gMCkpLCAzKSwKICAgICAgICAgICAgICAgICAgICAgICAjICAgcmVwKG5yb3coZmlsdGVyKGZpbmUuZ3JpZCwgcmFkYW4gPT0gMCkpLCAzKSkpCmVycm9yLnJhdGUKCmcudG90ID0gZ2dwbG90KGVycm9yLnJhdGUpICsKICBnZW9tX2NvbChtYXBwaW5nID0gYWVzKHggPSBzb3VyY2UsIHkgPSByYXRlLCBmaWxsID0gc291cmNlKSkgKwogIGdlb21fZXJyb3JiYXIobWFwcGluZyA9IGFlcyh4ID0gc291cmNlLCB5ID0gcmF0ZSwgeW1pbiA9IHJhdGUgLSBlcnIsIHltYXggPSByYXRlICsgZXJyKSwgd2lkdGggPSAwLjUpICsKICBmYWNldF93cmFwKH5jcml0ZXJpYSwgc2NhbGVzID0gJ2ZyZWUnKSArCiAgc2NhbGVfeF9kaXNjcmV0ZShicmVha3MgPSBjKCksIGxhYmVscyA9IGMoKSkgKwogIGxhYnMoeCA9ICcnLCB5ID0gJ1RvdGFsXG5FcnJvciBSYXRlJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKGxhYmVscyA9IGMoJzBwYXJldCcgPSAnU3RhcnRpbmcgRGF0YXNldCcsICcxYWRhcHQnID0gJysgQWRhcHRpdmUgU2FtcGxpbmcnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJzJyYW5kbycgPSAnKyBSYW5kb20gU2FtcGxpbmcnKSwgbmFtZSA9ICcnLAogICAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gYygnMHBhcmV0JyA9ICdza3libHVlMicsICcxYWRhcHQnID0gJ3JlZCcsICcycmFuZG8nID0gJ2dyZWVuJykpICsKICB0aGVtZV9jbGFzc2ljKCkgKyBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSBjKDAsIC4xKSkpCmcudHkxID0gZ2dwbG90KGVycm9yLnJhdGUpICsKICBnZW9tX2NvbChtYXBwaW5nID0gYWVzKHggPSBzb3VyY2UsIHkgPSB0eXAxLCBmaWxsID0gc291cmNlKSkgKwogIGdlb21fZXJyb3JiYXIobWFwcGluZyA9IGFlcyh4ID0gc291cmNlLCB5ID0gdHlwMSwgeW1pbiA9IHR5cDEgLSB0eXAxLmVyciwgeW1heCA9IHR5cDEgKyB0eXAxLmVyciksIHdpZHRoID0gMC41KSArCiAgZmFjZXRfd3JhcCh+Y3JpdGVyaWEsIHNjYWxlcyA9ICdmcmVlJykgKwogIHNjYWxlX3hfZGlzY3JldGUoYnJlYWtzID0gYygpLCBsYWJlbHMgPSBjKCkpICsKICBsYWJzKHggPSAnJywgeSA9ICdGYWxzZSBQb3NpdGl2ZVxuRXJyb3IgUmF0ZScpICsKICBzY2FsZV9maWxsX21hbnVhbChsYWJlbHMgPSBjKCcwcGFyZXQnID0gJ1N0YXJ0aW5nIERhdGFzZXQnLCAnMWFkYXB0JyA9ICcrIEFkYXB0aXZlIFNhbXBsaW5nJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICcycmFuZG8nID0gJysgUmFuZG9tIFNhbXBsaW5nJyksIG5hbWUgPSAnJywKICAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGMoJzBwYXJldCcgPSAnc2t5Ymx1ZTInLCAnMWFkYXB0JyA9ICdyZWQnLCAnMnJhbmRvJyA9ICdncmVlbicpKSArCiAgdGhlbWVfY2xhc3NpYygpICsgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gYygwLCAuMSkpKQpnLnR5MiA9IGdncGxvdChlcnJvci5yYXRlKSArCiAgZ2VvbV9jb2wobWFwcGluZyA9IGFlcyh4ID0gc291cmNlLCB5ID0gdHlwMiwgZmlsbCA9IHNvdXJjZSkpICsKICBnZW9tX2Vycm9yYmFyKG1hcHBpbmcgPSBhZXMoeCA9IHNvdXJjZSwgeSA9IHR5cDIsIHltaW4gPSB0eXAyIC0gdHlwMi5lcnIsIHltYXggPSB0eXAyICsgdHlwMi5lcnIpLCB3aWR0aCA9IDAuNSkgKwogIGZhY2V0X3dyYXAofmNyaXRlcmlhLCBzY2FsZXMgPSAnZnJlZScpICsKICBzY2FsZV94X2Rpc2NyZXRlKGJyZWFrcyA9IGMoKSwgbGFiZWxzID0gYygpKSArCiAgbGFicyh4ID0gJycsIHkgPSAnRmFsc2UgTmVnYXRpdmVcbkVycm9yIFJhdGUnKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobGFiZWxzID0gYygnMHBhcmV0JyA9ICdTdGFydGluZyBEYXRhc2V0JywgJzFhZGFwdCcgPSAnKyBBZGFwdGl2ZSBTYW1wbGluZycsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnMnJhbmRvJyA9ICcrIFJhbmRvbSBTYW1wbGluZycpLCBuYW1lID0gJycsCiAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBjKCcwcGFyZXQnID0gJ3NreWJsdWUyJywgJzFhZGFwdCcgPSAncmVkJywgJzJyYW5kbycgPSAnZ3JlZW4nKSkgKwogIHRoZW1lX2NsYXNzaWMoKSArIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBleHBhbnNpb24obXVsdCA9IGMoMCwgLjEpKSkKCihnLnRvdCArIGd1aWRlcyhmaWxsID0gRkFMU0UpKSAvIGcudHkxIC8gKGcudHkyICsgZ3VpZGVzKGZpbGwgPSBGQUxTRSkpCgp3cml0ZS5jc3YoZXJyb3IucmF0ZSwgJ0Vycm9yUmF0ZXMuY3N2JykKcm0oZy50b3QsIGcudHkxLCBnLnR5MikKYGBgCgpBIGNsb3NlciBpbnNwZWN0aW9uIG9mIGVhY2ggR1AtZGlzY292ZXJlZCBib3VuZGFyeSBjb21wYXJlZCB0byB0aGUgYWN0dWFsIGJvdW5kYXJ5IGJhc2VkIG9uIGZpbmUtcmVzb2x1dGlvbiBmdW5jdGlvbiBldmFsdWF0aW9uLgoKYGBge3IgR1AgTWFyZ2luYWxzIENvbXBhcmlzb259CiMgU2luZ2xlIHZhcmlhYmxlIGNvbmRpdGlvbmFscyBjb21wYXJlZCB0byB0aGUgZXhwZWN0ZWQgY29uZGl0aW9uYWxzIGJ5IGludGVncmF0aW9uCkluZmVyLnBsdCA9IHJlYWQuY3N2KCcuLi9FeF9RdWFydGljL01hcmdpbmFsc19kZWx0YS5jc3YnKQpnZ3Bsb3QoKSArCiAgZ2VvbV9wYXRoKGRhdGEgPSBmaWx0ZXIoSW5mZXIucGx0LCBjYXQgPT0gJ3RydScpLCBtYXBwaW5nID0gYWVzKHggPSB4LCB5ID0gcHJvYiAtIHBzZCwgY29sb3IgPSBjYXQpLCBsaW5ldHlwZSA9IDIpICsKICBnZW9tX3BhdGgoZGF0YSA9IGZpbHRlcihJbmZlci5wbHQsIGNhdCA9PSAndHJ1JyksIG1hcHBpbmcgPSBhZXMoeCA9IHgsIHkgPSBwcm9iICsgcHNkLCBjb2xvciA9IGNhdCksIGxpbmV0eXBlID0gMikgKwogIGdlb21fcGF0aChkYXRhID0gZmlsdGVyKEluZmVyLnBsdCwgY2F0ICE9ICd0cnUnKSwgbWFwcGluZyA9IGFlcyh4ID0geCwgeSA9IHByb2IsIGNvbG9yID0gY2F0KSkgKwogIGZhY2V0X3dyYXAodmFyfi4sIG5yb3cgPSAyKSArIAogIHNjYWxlX2NvbG9yX21hbnVhbChsYWJlbHMgPSBjKCd0cnUnID0gJ0V4cGVjdGVkIE1hcmdpbmFsJywgJ3N0YXJ0JyA9ICdTdGFydGluZyBEYXRhc2V0JywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnYWRhcHQnID0gJysgQWRhcHRpdmUgU2FtcGxpbmcnLCAncmFuZG8nID0gJysgUmFuZG9tIFNhbXBsaW5nJyksCiAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGMoJ3RydScgPSAnYmxhY2snLCAnc3RhcnQnID0gJ3NreWJsdWUyJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnYWRhcHQnID0gJ3JlZCcsICdyYW5kbycgPSAnZ3JlZW4nKSwKICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYygndHJ1JywgJ3N0YXJ0JywgJ2FkYXB0JywgJ3JhbmRvJykpICsKICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChsaW5ldHlwZSA9IGMoMiwgMSwgMSwgMSkpKSkgKwogIGxhYnMoeCA9ICcnLCB5ID0gJ1Byb2JhYmlsaXR5IG9mIEFjY2VwdGFuY2UnLCBzdWJ0aXRsZSA9IGV4cHJlc3Npb24oJ1BhcmV0byBEaXN0YW5jZScpLCBjb2xvciA9ICcnKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMC4wNSkpICsgc2NhbGVfeF9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCkpICsKICB0aGVtZV9idygpCgpJbmZlci5wbHQgPSByZWFkLmNzdignLi4vRXhfUXVhcnRpYy9NYXJnaW5hbHNfY3V0b2YuY3N2JykKZ2dwbG90KCkgKwogIGdlb21fcGF0aChkYXRhID0gZmlsdGVyKEluZmVyLnBsdCwgY2F0ID09ICd0cnUnKSwgbWFwcGluZyA9IGFlcyh4ID0geCwgeSA9IHByb2IgLSBwc2QsIGNvbG9yID0gY2F0KSwgbGluZXR5cGUgPSAyKSArCiAgZ2VvbV9wYXRoKGRhdGEgPSBmaWx0ZXIoSW5mZXIucGx0LCBjYXQgPT0gJ3RydScpLCBtYXBwaW5nID0gYWVzKHggPSB4LCB5ID0gcHJvYiArIHBzZCwgY29sb3IgPSBjYXQpLCBsaW5ldHlwZSA9IDIpICsKICBnZW9tX3BhdGgoZGF0YSA9IGZpbHRlcihJbmZlci5wbHQsIGNhdCAhPSAndHJ1JyksIG1hcHBpbmcgPSBhZXMoeCA9IHgsIHkgPSBwcm9iLCBjb2xvciA9IGNhdCkpICsKICBmYWNldF93cmFwKHZhcn4uLCBucm93ID0gMikgKyAKICBzY2FsZV9jb2xvcl9tYW51YWwobGFiZWxzID0gYygndHJ1JyA9ICdFeHBlY3RlZCBNYXJnaW5hbCcsICdzdGFydCcgPSAnU3RhcnRpbmcgRGF0YXNldCcsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2FkYXB0JyA9ICcrIEFkYXB0aXZlIFNhbXBsaW5nJywgJ3JhbmRvJyA9ICcrIFJhbmRvbSBTYW1wbGluZycpLAogICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBjKCd0cnUnID0gJ2JsYWNrJywgJ3N0YXJ0JyA9ICdza3libHVlMicsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2FkYXB0JyA9ICdyZWQnLCAncmFuZG8nID0gJ2dyZWVuJyksCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoJ3RydScsICdzdGFydCcsICdhZGFwdCcsICdyYW5kbycpKSArCiAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3QobGluZXR5cGUgPSBjKDIsIDEsIDEsIDEpKSkpICsKICBsYWJzKHggPSAnJywgeSA9ICdQcm9iYWJpbGl0eSBvZiBBY2NlcHRhbmNlJywgc3VidGl0bGUgPSBleHByZXNzaW9uKCdDdXRvZmYgVGhyZXNob2xkcycpLCBjb2xvciA9ICcnKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMC4wNSkpICsgc2NhbGVfeF9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCkpICsKICB0aGVtZV9idygpCgpJbmZlci5wbHQgPSByZWFkLmNzdignLi4vRXhfUXVhcnRpYy9NYXJnaW5hbHNfcmFkYW4uY3N2JykKZ2dwbG90KCkgKwogIGdlb21fcGF0aChkYXRhID0gZmlsdGVyKEluZmVyLnBsdCwgY2F0ID09ICd0cnUnKSwgbWFwcGluZyA9IGFlcyh4ID0geCwgeSA9IHByb2IgLSBwc2QsIGNvbG9yID0gY2F0KSwgbGluZXR5cGUgPSAyKSArCiAgZ2VvbV9wYXRoKGRhdGEgPSBmaWx0ZXIoSW5mZXIucGx0LCBjYXQgPT0gJ3RydScpLCBtYXBwaW5nID0gYWVzKHggPSB4LCB5ID0gcHJvYiArIHBzZCwgY29sb3IgPSBjYXQpLCBsaW5ldHlwZSA9IDIpICsKICBnZW9tX3BhdGgoZGF0YSA9IGZpbHRlcihJbmZlci5wbHQsIGNhdCAhPSAndHJ1JyksIG1hcHBpbmcgPSBhZXMoeCA9IHgsIHkgPSBwcm9iLCBjb2xvciA9IGNhdCkpICsKICBmYWNldF93cmFwKHZhcn4uLCBucm93ID0gMiwgbGFiZWxsZXIgPSBsYWJlbF9wYXJzZWQpICsgCiAgc2NhbGVfY29sb3JfbWFudWFsKGxhYmVscyA9IGMoJ3RydScgPSAnRXhwZWN0ZWQgTWFyZ2luYWwnLCAnc3RhcnQnID0gJ1N0YXJ0aW5nIERhdGFzZXQnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdhZGFwdCcgPSAnKyBBZGFwdGl2ZSBTYW1wbGluZycsICdyYW5kbycgPSAnKyBSYW5kb20gU2FtcGxpbmcnKSwKICAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gYygndHJ1JyA9ICdibGFjaycsICdzdGFydCcgPSAnc2t5Ymx1ZTInLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdhZGFwdCcgPSAncmVkJywgJ3JhbmRvJyA9ICdncmVlbicpLAogICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKCd0cnUnLCAnc3RhcnQnLCAnYWRhcHQnLCAncmFuZG8nKSkgKwogIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KGxpbmV0eXBlID0gYygyLCAxLCAxLCAxKSkpKSArCiAgbGFicyh4ID0gJycsIHkgPSAnUHJvYmFiaWxpdHkgb2YgQWNjZXB0YW5jZScsIHN1YnRpdGxlID0gZXhwcmVzc2lvbignVXRvcGlhIERpc3RhbmNlICsgUHJpb3JpdHknKSwgY29sb3IgPSAnJykgKwogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBjKDAsIDAuMDUpKSArIHNjYWxlX3hfY29udGludW91cyhleHBhbmQgPSBjKDAsIDApKSArCiAgdGhlbWVfYncoKQoKCmBgYAoKIyBFeGlzdGluZyBNZXRob2Q6IFN1cHBvcnQgVmVjdG9yIE1hY2hpbmVzCgpUaGUgbWV0aG9kIHNob3VsZCB1c2Ugb25seSB0aGUgZGF0YSBmcm9tIHRoZSBpbml0aWFsIFBhcmV0byBmcm9udCBjYWxjdWxhdGlvbi4gCkFkZGl0aW9uYWwgc2FtcGxlcyBtYXkgYmUgbmVjZXNzYXJ5IHRvIHJlZmluZSB0aGUgcmVzdWx0cywgYnV0IHRoZXJlIGlzIG5vIGVzdGFibGlzaGVkIG1ldGhvZCBmb3IgdGhpcyByZWZpbmVtZW50IGFzIG9mIHlldC4KClNWTSBtZXRob2RzIHJlcXVpcmUgZXhpc3RpbmcgbGFiZWxzIGZvciB0aGUgcG9pbnRzOyBpbiB0aGlzIGNhc2UsIHRoZSBsYWJlbHMgYXJlIHdoZXRoZXIgb3Igbm90IHRoZSBwb2ludCBtZWV0cyB0aGUgc2FtZSB0aHJlZSBzZWxlY3Rpb24gY3JpdGVyaWEgYXMgdGVzdGVkIHdpdGggdGhlIG5ldyBHUCBtZXRob2QuCgpTaWdtb2lkYWwga2VybmVscyBhbHdheXMgZ2l2ZSB2ZXJ5IGJhZCBmaXRzLCBzbyBpZ25vcmluZyB0aG9zZSBwb3NzaWJpbGl0aWVzIGluIHRoZSBlcnJvciBjYWxjdWxhdGlvbi4KCmBgYHtyIFNWTTogRnVuY3Rpb25zfQojIENyZWF0ZSBhIGZ1bmN0aW9uIHRvIG91dHB1dCB0aGUgcGxvdCBvZiBhbGwgNCBtb2RlbHMgY29tcGFyZWQgdG8gdGhlIHJlYWwgcmVzdWx0ClNWTS5pbnB1dCA9IGZ1bmN0aW9uKGZpbmUuaW5wdXQsIG1vZC5yYWQsIG1vZC5saW4sIG1vZC5wb2wsIG1vZC5zaWcsIHRpdCl7CiAgIyBGaW5lIGlucHV0IGhhcyB0aGUgdmFyaWFibGVzIHgxLCB4MiwgYW5kIGNhdAogIHJlcyA9IHByZWRpY3Qob2JqZWN0ID0gbW9kLmxpbiwgbmV3ZGF0YSA9IGZpbmUuaW5wdXRbLCBjKCd4MScsICd4MicpXSkKICBmaW5lLmlucHV0JHJlcyA9IHJlcwogIGcubGluID0gZ2dwbG90KGZpbmUuaW5wdXQpICsKICAgIGdlb21fcG9pbnQoZGF0YSA9IGZpbmUuaW5wdXQsIG1hcHBpbmcgPSBhZXMoeCA9IHgxLCB5ID0geDIsIGNvbG9yID0gcmVzKSkgKwogICAgZ2VvbV9jb250b3VyKGRhdGEgPSBmaW5lLmlucHV0LCBtYXBwaW5nID0gYWVzKHggPSB4MSwgeSA9IHgyLCB6ID0gY2F0KSwgYnJlYWtzID0gYygwLjUpKSArCiAgICBsYWJzKHN1YnRpdGxlID0gJ0xpbmVhcicsIHggPSBleHByZXNzaW9uKCd4J1sxXSksIHkgPSBleHByZXNzaW9uKCd4J1syXSkpICsgCiAgICBndWlkZXMoY29sb3IgPSBGQUxTRSkKICByZXMgPSBwcmVkaWN0KG9iamVjdCA9IG1vZC5yYWQsIG5ld2RhdGEgPSBmaW5lLmlucHV0WyxjKCd4MScsICd4MicpXSkKICBmaW5lLmlucHV0JHJlcyA9IHJlcwogIGcucmFkID0gZ2dwbG90KCkgKwogICAgZ2VvbV9wb2ludChkYXRhID0gZmluZS5pbnB1dCwgbWFwcGluZyA9IGFlcyh4ID0geDEsIHkgPSB4MiwgY29sb3IgPSByZXMpKSArCiAgICBnZW9tX2NvbnRvdXIoZGF0YSA9IGZpbmUuaW5wdXQsIG1hcHBpbmcgPSBhZXMoeCA9IHgxLCB5ID0geDIsIHogPSBjYXQpLCBicmVha3MgPSBjKDAuNSkpICsKICAgIGxhYnMoc3VidGl0bGUgPSAnUmFkaWFsJywgeCA9IGV4cHJlc3Npb24oJ3gnWzFdKSwgeSA9IGV4cHJlc3Npb24oJ3gnWzJdKSwgdGl0bGUgPSB0aXQpICsgZ3VpZGVzKGNvbG9yID0gRkFMU0UpCiAgcmVzID0gcHJlZGljdChvYmplY3QgPSBtb2QucG9sLCBuZXdkYXRhID0gZmluZS5pbnB1dFssYygneDEnLCAneDInKV0pCiAgZmluZS5pbnB1dCRyZXMgPSByZXMKICBnLnBvbCA9IGdncGxvdCgpICsKICAgIGdlb21fcG9pbnQoZGF0YSA9IGZpbmUuaW5wdXQsIG1hcHBpbmcgPSBhZXMoeCA9IHgxLCB5ID0geDIsIGNvbG9yID0gcmVzKSkgKwogICAgZ2VvbV9jb250b3VyKGRhdGEgPSBmaW5lLmlucHV0LCBtYXBwaW5nID0gYWVzKHggPSB4MSwgeSA9IHgyLCB6ID0gY2F0KSwgYnJlYWtzID0gYygwLjUpKSArCiAgICBsYWJzKHN1YnRpdGxlID0gJ1BvbHlub21pYWwnLCB4ID0gZXhwcmVzc2lvbigneCdbMV0pLCB5ID0gZXhwcmVzc2lvbigneCdbMl0pKSArIGd1aWRlcyhjb2xvciA9IEZBTFNFKQogIHJlcyA9IHByZWRpY3Qob2JqZWN0ID0gbW9kLnNpZywgbmV3ZGF0YSA9IGZpbmUuaW5wdXRbLGMoJ3gxJywgJ3gyJyldKQogIGZpbmUuaW5wdXQkcmVzID0gcmVzCiAgZy5zaWcgPSBnZ3Bsb3QoKSArCiAgICBnZW9tX3BvaW50KGRhdGEgPSBmaW5lLmlucHV0LCBtYXBwaW5nID0gYWVzKHggPSB4MSwgeSA9IHgyLCBjb2xvciA9IHJlcykpICsKICAgIGdlb21fY29udG91cihkYXRhID0gZmluZS5pbnB1dCwgbWFwcGluZyA9IGFlcyh4ID0geDEsIHkgPSB4MiwgeiA9IGNhdCksIGJyZWFrcyA9IGMoMC41KSkgKwogICAgbGFicyhzdWJ0aXRsZSA9ICdTaWdtb2lkJywgeCA9IGV4cHJlc3Npb24oJ3gnWzFdKSwgeSA9IGV4cHJlc3Npb24oJ3gnWzJdKSkgKyBndWlkZXMoY29sb3IgPSBGQUxTRSkKICByZXR1cm4oKGcucmFkICsgZy5saW4pIC8gKGcucG9sICsgZy5zaWcpKQp9CgpTVk0ubW9kLmFsbCA9IGZ1bmN0aW9uKGlucHV0LmRhdGEpewogICMgR2l2ZW4gaW5wdXQgZGF0YSwgZmluZCBhbGwgNCBkZWZhdWx0IG1vZGVscyBhbmQgcmV0dXJuIGFzIGEgbGlzdAogIGZpdC5yYWQgPSBzdm0oYXMuZmFjdG9yKGNhdCkgfiB4MSp4MiwgZGF0YSA9IGlucHV0LmRhdGFbLGMoJ3gxJywgJ3gyJywgJ2NhdCcpXSwgCiAgICAgICAgICAgIHNjYWxlID0gRkFMU0UsIGtlcm5lbCA9ICJyYWRpYWwiLCBjb3N0ID0gNSkKICBmaXQubGluID0gc3ZtKGFzLmZhY3RvcihjYXQpIH4geDEqeDIsIGRhdGEgPSBpbnB1dC5kYXRhWyxjKCd4MScsICd4MicsICdjYXQnKV0sIAogICAgICAgICAgICBzY2FsZSA9IEZBTFNFLCBrZXJuZWwgPSAibGluZWFyIiwgY29zdCA9IDUpCiAgIyBQb2x5bm9taWFsOiBpbmNyZWFzZSB0aGUga2VybmVsIGRlZ3JlZSBkdWUgdG8gY29tcGxleGl0eSBvZiB0aGUgYm91bmRhcnk7IG1heGltdW0gdGVzdGVkIHRoYXQgc3RpbGwgYWNoaWV2ZWQgY29udmVyZ2VuY2UKICBmaXQucG9sID0gc3ZtKGFzLmZhY3RvcihjYXQpIH4geDEqeDIsIGRhdGEgPSBpbnB1dC5kYXRhWyxjKCd4MScsICd4MicsICdjYXQnKV0sIAogICAgICAgICAgICBzY2FsZSA9IEZBTFNFLCBrZXJuZWwgPSAicG9seW5vbWlhbCIsIGNvc3QgPSA1LCBkZWdyZWUgPSAzLjUpCiAgIyBJbmNyZWVhc2UgdGhlIGNvZWZmaWNpZW50CiAgZml0LnNpZyA9IHN2bShhcy5mYWN0b3IoY2F0KSB+IHgxKngyLCBkYXRhID0gaW5wdXQuZGF0YVssYygneDEnLCAneDInLCAnY2F0JyldLCAKICAgICAgICAgICAgc2NhbGUgPSBGQUxTRSwga2VybmVsID0gInNpZ21vaWQiLCBjb3N0ID0gNSkKICByZXR1cm4obGlzdChyYWQgPSBmaXQucmFkLCBsaW4gPSBmaXQubGluLCBwb2wgPSBmaXQucG9sLCBzaWcgPSBmaXQuc2lnKSkKfQpTVk0uZXJyID0gZnVuY3Rpb24oZmluZS5pbnB1dCwgbW9kLmxpc3QpewogICMgRmluZSBncmlkIGhhcyB0aGUgcmVhbCByZXN1bHQ7IG1vZC5saXN0IGlzIHRoZSA0IGRpZmZlcmVudCBTVk0ga2VybmVscwogIGZpbmUuaW5wdXQkcmFkID0gcHJlZGljdChvYmplY3QgPSBtb2QubGlzdCRyYWQsIG5ld2RhdGEgPSBmaW5lLmlucHV0WyxjKCd4MScsICd4MicpXSkKICBmaW5lLmlucHV0JGxpbiA9IHByZWRpY3Qob2JqZWN0ID0gbW9kLmxpc3QkbGluLCBuZXdkYXRhID0gZmluZS5pbnB1dFssYygneDEnLCAneDInKV0pCiAgZmluZS5pbnB1dCRwb2wgPSBwcmVkaWN0KG9iamVjdCA9IG1vZC5saXN0JHBvbCwgbmV3ZGF0YSA9IGZpbmUuaW5wdXRbLGMoJ3gxJywgJ3gyJyldKQogICMgZmluZS5pbnB1dCRzaWcgPSBwcmVkaWN0KG9iamVjdCA9IG1vZC5saXN0JHNpZywgbmV3ZGF0YSA9IGZpbmUuaW5wdXRbLGMoJ3gxJywgJ3gyJyldKQogIAogICMgRXJyb3IgcmF0ZQogIHJhZCA9IG5yb3coZmlsdGVyKGZpbmUuaW5wdXQsIGNhdCA9PSAwLCByYWQgPT0gMSkpICsgbnJvdyhmaWx0ZXIoZmluZS5pbnB1dCwgY2F0ID09IDEsIHJhZCA9PSAwKSkKICBsaW4gPSBucm93KGZpbHRlcihmaW5lLmlucHV0LCBjYXQgPT0gMCwgbGluID09IDEpKSArIG5yb3coZmlsdGVyKGZpbmUuaW5wdXQsIGNhdCA9PSAxLCBsaW4gPT0gMCkpCiAgcG9sID0gbnJvdyhmaWx0ZXIoZmluZS5pbnB1dCwgY2F0ID09IDAsIHBvbCA9PSAxKSkgKyBucm93KGZpbHRlcihmaW5lLmlucHV0LCBjYXQgPT0gMSwgcG9sID09IDApKQogICMgc2lnID0gbnJvdyhmaWx0ZXIoZmluZS5pbnB1dCwgY2F0ID09IDAsIHNpZyA9PSAxKSkgKyBucm93KGZpbHRlcihmaW5lLmlucHV0LCBjYXQgPT0gMSwgc2lnID09IDApKQogICMgcmF0ZSA9IGMocmFkLCBsaW4sIHBvbCwgc2lnKS9ucm93KGZpbmUuaW5wdXQpCiAgcmF0ZSA9IGMocmFkLCBsaW4sIHBvbCkvbnJvdyhmaW5lLmlucHV0KQogICMgVW5jZXJ0YWludHkKICBlcnIgPSBzcXJ0KHJhdGUqKDEtcmF0ZSkvbnJvdyhmaW5lLmlucHV0KSkKICAKICAjIFR5cGUgSSBlcnJvciA9IFBbc2hvdWxkIHJlamVjdCB8IGFjY2VwdGVkXSA9IFBbYm90aF0gUFtzaG91bGQgcmVqZWN0XSAvIFBbYWNjZXB0ZWRdCiAgdHlwMSA9CiAgICBjKG5yb3coZmlsdGVyKGZpbmUuaW5wdXQsIGNhdCA9PSAwLCByYWQgPT0gMSkpKm5yb3coZmlsdGVyKGZpbmUuaW5wdXQsIGNhdCA9PSAwKSkvbnJvdyhmaW5lLmlucHV0KS8KICAgICAgICBucm93KGZpbHRlcihmaW5lLmlucHV0LCByYWQgPT0gMSkpLAogICAgICBucm93KGZpbHRlcihmaW5lLmlucHV0LCBjYXQgPT0gMCwgbGluID09IDEpKSpucm93KGZpbHRlcihmaW5lLmlucHV0LCBjYXQgPT0gMCkpL25yb3coZmluZS5pbnB1dCkvCiAgICAgICAgbnJvdyhmaWx0ZXIoZmluZS5pbnB1dCwgbGluID09IDEpKSwKICAgICAgbnJvdyhmaWx0ZXIoZmluZS5pbnB1dCwgY2F0ID09IDAsIHBvbCA9PSAxKSkqbnJvdyhmaWx0ZXIoZmluZS5pbnB1dCwgY2F0ID09IDApKS9ucm93KGZpbmUuaW5wdXQpLwogICAgICAgIG5yb3coZmlsdGVyKGZpbmUuaW5wdXQsIHBvbCA9PSAxKSkpIywKICAgICAgIyBucm93KGZpbHRlcihmaW5lLmlucHV0LCBjYXQgPT0gMCwgc2lnID09IDEpKSpucm93KGZpbHRlcihmaW5lLmlucHV0LCBjYXQgPT0gMCkpL25yb3coZmluZS5pbnB1dCkvCiAgICAgICAgIyBucm93KGZpbHRlcihmaW5lLmlucHV0LCBzaWcgPT0gMSkpICkKICB0eXAxLmVyciA9IHNxcnQodHlwMSooMS10eXAxKSAvIG5yb3coZmluZS5pbnB1dCkpICAKICAKICAjIFR5cGUgSUkgZXJyb3IgPSBQW3Nob3VsZCBhY2NlcHQgfCByZWplY3RlZF0gPSBQW2JvdGhdIFBbc2hvdWxkIGFjY2VwdF0gLyBQW3JlamVjdGVkXQogIHR5cDIgPQogICAgYyhucm93KGZpbHRlcihmaW5lLmlucHV0LCBjYXQgPT0gMSwgcmFkID09IDApKSpucm93KGZpbHRlcihmaW5lLmlucHV0LCBjYXQgPT0gMSkpL25yb3coZmluZS5pbnB1dCkvCiAgICAgICAgbnJvdyhmaWx0ZXIoZmluZS5pbnB1dCwgcmFkID09IDApKSwKICAgICAgbnJvdyhmaWx0ZXIoZmluZS5pbnB1dCwgY2F0ID09IDEsIGxpbiA9PSAwKSkqbnJvdyhmaWx0ZXIoZmluZS5pbnB1dCwgY2F0ID09IDEpKS9ucm93KGZpbmUuaW5wdXQpLwogICAgICAgIG5yb3coZmlsdGVyKGZpbmUuaW5wdXQsIGxpbiA9PSAwKSksCiAgICAgIG5yb3coZmlsdGVyKGZpbmUuaW5wdXQsIGNhdCA9PSAxLCBwb2wgPT0gMCkpKm5yb3coZmlsdGVyKGZpbmUuaW5wdXQsIGNhdCA9PSAxKSkvbnJvdyhmaW5lLmlucHV0KS8KICAgICAgICBucm93KGZpbHRlcihmaW5lLmlucHV0LCBwb2wgPT0gMCkpKSMsCiAgICAgICMgbnJvdyhmaWx0ZXIoZmluZS5pbnB1dCwgY2F0ID09IDEsIHNpZyA9PSAwKSkqbnJvdyhmaWx0ZXIoZmluZS5pbnB1dCwgY2F0ID09IDEpKS9ucm93KGZpbmUuaW5wdXQpLwogICAgICAgICMgbnJvdyhmaWx0ZXIoZmluZS5pbnB1dCwgc2lnID09IDApKSApCiAgdHlwMi5lcnIgPSBzcXJ0KHR5cDIqKDEtdHlwMikgLyBucm93KGZpbmUuaW5wdXQpKQogICMgcmV0dXJuKGRhdGEuZnJhbWUobWV0aG9kID0gYygnU1ZNLXJhZCcsICdTVk0tbGluJywgJ1NWTS1wb2wnLCAnU1ZNLXNpZycpLCByYXRlLCBlcnIsIHR5cDEsIHR5cDEuZXJyLCB0eXAyLCB0eXAyLmVycikpCiAgcmV0dXJuKGRhdGEuZnJhbWUobWV0aG9kID0gYygnU1ZNLXJhZCcsICdTVk0tbGluJywgJ1NWTS1wb2wnKSwgcmF0ZSwgZXJyLCB0eXAxLCB0eXAxLmVyciwgdHlwMiwgdHlwMi5lcnIpKQp9CgojIEZvciB0aGUgcmFkaWFsIGFuZCBwb2x5bm9taWFsIGtlcm5lbHMsIGNhbGN1bGF0ZSB0aGUgbWFyZ2luYWxzIGFuZCBTaGFwbGV5IHZhbHVlcyBmb3IgY29tcGFyaXNvbgptYXJnaW5hbCA9IGZ1bmN0aW9uKG1vZCl7CiAgeC5ybmcgPSBzZXEoZnJvbSA9IDAsIHRvID0gNSwgbGVuZ3RoLm91dCA9IDUwKQogIHN2bS5tYXJnaW4gPSBkYXRhLmZyYW1lKCkKICBuc2FtcCA9IDIwMDAKICBmb3IoeCBpbiB4LnJuZyl7CiAgICAjIHgxIG1hcmdpbmFsCiAgICB0ZW1wLmZyYW1lID0gZGF0YS5mcmFtZSh4MSA9IHgsIHgyID0gcnVuaWYobiA9IG5zYW1wLCBtaW4gPSAwLCBtYXggPSA1KSkKICAgIHJlcyA9IGFzLm51bWVyaWMocHJlZGljdChvYmplY3QgPSBtb2QkcG9sLCBuZXdkYXRhID0gdGVtcC5mcmFtZSkpLTEKICAgIHN2bS5tYXJnaW4gPSByYmluZChzdm0ubWFyZ2luLCBkYXRhLmZyYW1lKHggPSB4LCB2YXIgPSAneDEnLCBwcm9iID0gc3VtKHJlcykvbGVuZ3RoKHJlcyksIG1ldGhvZCA9ICdTVk0tcG9sJykpCiAgICByZXMgPSBhcy5udW1lcmljKHByZWRpY3Qob2JqZWN0ID0gbW9kJHJhZCwgbmV3ZGF0YSA9IHRlbXAuZnJhbWUpKS0xCiAgICBzdm0ubWFyZ2luID0gcmJpbmQoc3ZtLm1hcmdpbiwgZGF0YS5mcmFtZSh4ID0geCwgdmFyID0gJ3gxJywgcHJvYiA9IHN1bShyZXMpL2xlbmd0aChyZXMpLCBtZXRob2QgPSAnU1ZNLXJhZCcpKQogICAgIyB4MSBtYXJnaW5hbAogICAgdGVtcC5mcmFtZSA9IGRhdGEuZnJhbWUoeDIgPSB4LCB4MSA9IHJ1bmlmKG4gPSBuc2FtcCwgbWluID0gMCwgbWF4ID0gNSkpCiAgICByZXMgPSBhcy5udW1lcmljKHByZWRpY3Qob2JqZWN0ID0gbW9kJHBvbCwgbmV3ZGF0YSA9IHRlbXAuZnJhbWUpKS0xCiAgICBzdm0ubWFyZ2luID0gcmJpbmQoc3ZtLm1hcmdpbiwgZGF0YS5mcmFtZSh4ID0geCwgdmFyID0gJ3gyJywgcHJvYiA9IHN1bShyZXMpL2xlbmd0aChyZXMpLCBtZXRob2QgPSAnU1ZNLXBvbCcpKQogICAgcmVzID0gYXMubnVtZXJpYyhwcmVkaWN0KG9iamVjdCA9IG1vZCRyYWQsIG5ld2RhdGEgPSB0ZW1wLmZyYW1lKSktMQogICAgc3ZtLm1hcmdpbiA9IHJiaW5kKHN2bS5tYXJnaW4sIGRhdGEuZnJhbWUoeCA9IHgsIHZhciA9ICd4MicsIHByb2IgPSBzdW0ocmVzKS9sZW5ndGgocmVzKSwgbWV0aG9kID0gJ1NWTS1yYWQnKSkKICB9CiAgCiAgc3ZtLm1hcmdpbiRwc2QgPSBzcXJ0KHN2bS5tYXJnaW4kcHJvYiooMSAtIHN2bS5tYXJnaW4kcHJvYikvbnNhbXApCiAgcmV0dXJuKHN2bS5tYXJnaW4pCn0KClNWTS5zaGFwID0gZnVuY3Rpb24obW9kKXsKICAjIFN0cnVtYmVsaiBldCBhbC4gKDIwMTQpIE1vbnRlIENhcmxvIGVzdGltYXRlLgogICMgU2luY2UgdGhpcyBpcyBhIGNsYXNzaWZpY2F0aW9uIHNldHRpbmcsIG9ubHkgYSBkaWZmZXJlbmNlIG9mIDAgb3IgMSBpcyBwb3NzaWJsZSAKICAjIGkuZS4gYSBkaWZmZXJlbmNlIG9mIC0xIGlzIHRoZSBzYW1lIGFzIGEgZGlmZmVyZW5jZSBvZiAxLCBhcyBpdCBpcyBzaW1wbHkgYSBtaXNjbGFzc2lmaWNhdGlvbgogIG5zYW1wID0gMTUwMCo1MCAjIFNhbWUgbnVtYmVyIG9mIHNhbXBsZXMgYXMgb3RoZXIgbWV0aG9kcyBmb3IgY29uc2lzdGVuY3kKICB4MCA9IGRhdGEuZnJhbWUoeDEgPSBydW5pZihuID0gbnNhbXAsIG1pbiA9IDAsIG1heCA9IDUpLAogICAgICAgICAgICAgICAgICB4MiA9IHJ1bmlmKG4gPSBuc2FtcCwgbWluID0gMCwgbWF4ID0gNSkpCiAgejEgPSBkYXRhLmZyYW1lKHgxID0geDAkeDEsCiAgICAgICAgICAgICAgICAgIHgyID0gcnVuaWYobiA9IG5zYW1wLCBtaW4gPSAwLCBtYXggPSA1KSkKICB6MiA9IGRhdGEuZnJhbWUoeDEgPSBydW5pZihuID0gbnNhbXAsIG1pbiA9IDAsIG1heCA9IDUpLAogICAgICAgICAgICAgICAgICB4MiA9IHgwJHgyKQogICMgQXBwbHkgZnVuY3Rpb25zIHRvIGFsbAogIHJlcy5wb2wgPSBhcy5udW1lcmljKHByZWRpY3Qob2JqZWN0ID0gbW9kJHBvbCwgbmV3ZGF0YSA9IHgwKSkKICByZXMucmFkID0gYXMubnVtZXJpYyhwcmVkaWN0KG9iamVjdCA9IG1vZCRyYWQsIG5ld2RhdGEgPSB4MCkpCiAgeDAkcG9sID0gcmVzLnBvbDsgeDAkcmFkID0gcmVzLnJhZAogIHJlcy5wb2wgPSBhcy5udW1lcmljKHByZWRpY3Qob2JqZWN0ID0gbW9kJHBvbCwgbmV3ZGF0YSA9IHoxKSkKICByZXMucmFkID0gYXMubnVtZXJpYyhwcmVkaWN0KG9iamVjdCA9IG1vZCRyYWQsIG5ld2RhdGEgPSB6MSkpCiAgejEkcG9sID0gcmVzLnBvbDsgejEkcmFkID0gcmVzLnJhZAogIHJlcy5wb2wgPSBhcy5udW1lcmljKHByZWRpY3Qob2JqZWN0ID0gbW9kJHBvbCwgbmV3ZGF0YSA9IHoyKSkKICByZXMucmFkID0gYXMubnVtZXJpYyhwcmVkaWN0KG9iamVjdCA9IG1vZCRyYWQsIG5ld2RhdGEgPSB6MikpCiAgejIkcG9sID0gcmVzLnBvbDsgejIkcmFkID0gcmVzLnJhZAogICMgQ2FsY3VsYXRlIG1lYW4gYW5kIHN0YW5kYXJkIGVycm9yIG9mIHRoZSBkaWZmZXJlbmNlcyBmb3IgU2hhcGxleSB2YWx1ZXMKICBzaGFwID0gYyhtZWFuKCh4MCRwb2wgLSB6MSRwb2wpKSwgbWVhbigoeDAkcG9sIC0gejIkcG9sKSksCiAgICAgICAgICAgbWVhbigoeDAkcmFkIC0gejEkcmFkKSksIG1lYW4oKHgwJHJhZCAtIHoyJHJhZCkpKQogIHIuc2hhcCA9IGMoc2hhcFsxOjJdL21heChzaGFwWzE6Ml0pLCBzaGFwWzM6NF0vbWF4KHNoYXBbMzo0XSkpCiAgIyBGb3Igc3RhbmRhcmQgZXJyb3IsIGNhbGN1bGF0ZSBzdGFuZGFyZCBlcnJvciBvZiB0aGUgbWVhbiB3aXRoIG4gPSAxNTAwCiAgIyB0byBiZSBjb25zaXN0ZW50IHdpdGggdGhlIG90aGVyIHN0YW5kYXJkIGVycm9ycwogIHBvbC54MW0gPSBhcHBseShtYXRyaXgoeDAkcG9sIC0gejEkcG9sLCBucm93ID0gMTUwMCksIDIsIG1lYW4pCiAgcmFkLngxbSA9IGFwcGx5KG1hdHJpeCh4MCRyYWQgLSB6MSRyYWQsIG5yb3cgPSAxNTAwKSwgMiwgbWVhbikKICBwb2wueDJtID0gYXBwbHkobWF0cml4KHgwJHBvbCAtIHoyJHBvbCwgbnJvdyA9IDE1MDApLCAyLCBtZWFuKQogIHJhZC54Mm0gPSBhcHBseShtYXRyaXgoeDAkcmFkIC0gejIkcmFkLCBucm93ID0gMTUwMCksIDIsIG1lYW4pCiAgCiAgc2hhcC5lcnIgPSBjKHNkKHBvbC54MW0pLCBzZChwb2wueDJtKSwKICAgICAgICAgICAgICAgc2QocmFkLngxbSksIHNkKHJhZC54Mm0pKQogIHJldHVybihkYXRhLmZyYW1lKGltcG9ydCA9IHNoYXAsIHZhciA9IGMoJ3gxJywgJ3gyJyksIAogICAgICAgICAgICAgICAgICAgIHNkID0gc2hhcC5lcnIsIHIuaW1wb3J0ID0gci5zaGFwLCAKICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSBjKCdTaGFwbGV5LXBvbCcsICdTaGFwbGV5LXBvbCcsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1NoYXBsZXktcmFkJywgJ1NoYXBsZXktcmFkJykpKQp9CmBgYAoKYGBge3IgU1ZNOiBEaXN0YW5jZX0KZGF0YS5wYXJldCA9IHJlYWQuY3N2KCcuLi9FeF9RdWFydGljL0dQYXJfYWxsX3N0YXJ0LmNzdicpCmRhdGEuZGVsdGEgPSByZWFkLmNzdignLi4vRXhfUXVhcnRpYy9HUGFyX0FjY2VwdF9EZWx0YTEuY3N2JykKZGF0YS5yYW5kbyA9IHJlYWQuY3N2KGZpbGUgPSAnR1Bhcl9SYW5kb20uY3N2JykKCiMgRGVmaW5lIGFjY2VwdGFuY2UKZGF0YS5wYXJldCRjYXQgPSAwOyBkYXRhLmRlbHRhJGNhdCA9IDA7IGRhdGEucmFuZG8kY2F0ID0gMDsgZmluZS5ncmlkJGNhdCA9IDAKZGF0YS5wYXJldCRjYXRbZGF0YS5wYXJldCRkaXN0IDw9IDFdID0gMQpkYXRhLmRlbHRhJGNhdFtkYXRhLmRlbHRhJGRpc3QgPD0gMV0gPSAxCmRhdGEucmFuZG8kY2F0W2RhdGEucmFuZG8kZGlzdCA8PSAxXSA9IDEKZmluZS5ncmlkJGNhdFtmaW5lLmdyaWQkZGlzdCA8PSAxXSA9IDEKCiMgUGxvdHMKbW9kLnBhcmV0ID0gU1ZNLm1vZC5hbGwoaW5wdXQuZGF0YSA9IGRhdGEucGFyZXQpClNWTS5pbnB1dChmaW5lLmlucHV0ID0gZmluZS5ncmlkWyxjKCd4MScsICd4MicsICdjYXQnKV0sIAogICAgICAgICAgbW9kLnJhZCA9IG1vZC5wYXJldCRyYWQsIG1vZC5saW4gPSBtb2QucGFyZXQkbGluLCAKICAgICAgICAgIG1vZC5wb2wgPSBtb2QucGFyZXQkcG9sLCBtb2Quc2lnID0gbW9kLnBhcmV0JHNpZywKICAgICAgICAgIHRpdCA9ICdQYXJldG8gRGlzdGFuY2UsIFN0YXJ0aW5nIERhdGFzZXQnKQoKbW9kLmFkYXB0ID0gU1ZNLm1vZC5hbGwoaW5wdXQuZGF0YSA9IGRhdGEuZGVsdGEpCiMgU3RvcmUgdGhpcyBtb2RlbCBmb3IgaW1wb3J0YW5jZSByYW5raW5nIGxhdGVyCm1vZC5hZGFwdC5kZWx0YSA9IG1vZC5hZGFwdApTVk0uaW5wdXQoZmluZS5pbnB1dCA9IGZpbmUuZ3JpZFssYygneDEnLCAneDInLCAnY2F0JyldLCAKICAgICAgICAgIG1vZC5yYWQgPSBtb2QuYWRhcHQkcmFkLCBtb2QubGluID0gbW9kLmFkYXB0JGxpbiwgCiAgICAgICAgICBtb2QucG9sID0gbW9kLmFkYXB0JHBvbCwgbW9kLnNpZyA9IG1vZC5hZGFwdCRzaWcsCiAgICAgICAgICB0aXQgPSAnUGFyZXRvIERpc3RhbmNlLCArIEFkYXB0aXZlIFNhbXBsaW5nJykKCm1vZC5yYW5kbyA9IFNWTS5tb2QuYWxsKGlucHV0LmRhdGEgPSBkYXRhLnJhbmRvKQpTVk0uaW5wdXQoZmluZS5pbnB1dCA9IGZpbmUuZ3JpZFssYygneDEnLCAneDInLCAnY2F0JyldLCAKICAgICAgICAgIG1vZC5yYWQgPSBtb2QucmFuZG8kcmFkLCBtb2QubGluID0gbW9kLnJhbmRvJGxpbiwgCiAgICAgICAgICBtb2QucG9sID0gbW9kLnJhbmRvJHBvbCwgbW9kLnNpZyA9IG1vZC5yYW5kbyRzaWcsCiAgICAgICAgICB0aXQgPSAnUGFyZXRvIERpc3RhbmNlLCArIFJhbmRvbSBTYW1wbGluZycpCgplcnIucGFyZXQgPSBTVk0uZXJyKGZpbmUuaW5wdXQgPSBmaW5lLmdyaWQsIG1vZC5saXN0ID0gbW9kLnBhcmV0KQplcnIucGFyZXQkc291cmNlID0gJzBwYXJldCc7IGVyci5wYXJldCRjcml0ZXJpYSA9ICdQYXJldG8gRGlzdGFuY2UnCmVyci5hZGFwdCA9IFNWTS5lcnIoZmluZS5pbnB1dCA9IGZpbmUuZ3JpZCwgbW9kLmxpc3QgPSBtb2QuYWRhcHQpCmVyci5hZGFwdCRzb3VyY2UgPSAnMWFkYXB0JzsgZXJyLmFkYXB0JGNyaXRlcmlhID0gJ1BhcmV0byBEaXN0YW5jZScKZXJyLnJhbmRvID0gU1ZNLmVycihmaW5lLmlucHV0ID0gZmluZS5ncmlkLCBtb2QubGlzdCA9IG1vZC5yYW5kbykKZXJyLnJhbmRvJHNvdXJjZSA9ICcycmFuZG8nOyBlcnIucmFuZG8kY3JpdGVyaWEgPSAnUGFyZXRvIERpc3RhbmNlJwoKZXJyLnN2bS5kaXN0ID0gcmJpbmQoZXJyLnBhcmV0LCBlcnIuYWRhcHQsIGVyci5yYW5kbykKCmcucmF0ZSA9IGdncGxvdChlcnIuc3ZtLmRpc3QpICsKICBnZW9tX2NvbChtYXBwaW5nID0gYWVzKHggPSBzb3VyY2UsIHkgPSByYXRlLCBmaWxsID0gc291cmNlKSkgKwogIGdlb21fZXJyb3JiYXIobWFwcGluZyA9IGFlcyh4ID0gc291cmNlLCB5bWluID0gcmF0ZSAtIGVyciwgeW1heCA9IHJhdGUgKyBlcnIpLCB3aWR0aCA9IDAuNSkgKwogIGZhY2V0X3dyYXAofm1ldGhvZCwgbnJvdyA9IDEpICsKICBzY2FsZV9maWxsX21hbnVhbChsYWJlbHMgPSBjKCcwcGFyZXQnID0gJ1N0YXJ0aW5nIERhdGFzZXQnLCAnMWFkYXB0JyA9ICcrIEFkYXB0aXZlIFNhbXBsaW5nJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICcycmFuZG8nID0gJysgUmFuZG9tIFNhbXBsaW5nJyksIG5hbWUgPSAnJywKICAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGMoJzBwYXJldCcgPSAnc2t5Ymx1ZTInLCAnMWFkYXB0JyA9ICdyZWQnLCAnMnJhbmRvJyA9ICdncmVlbicpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gYygwLCAwLjA1KSkpICsgc2NhbGVfeF9kaXNjcmV0ZShicmVha3MgPSAnJykgKwogIHRoZW1lX2J3KCkgKyBsYWJzKHggPSAnJywgeSA9ICdUb3RhbFxuRXJyb3IgUmF0ZScsIHN1YnRpdGxlID0gJ1BhcmV0byBEaXN0YW5jZScpCgpnLnR5cDEgPSBnZ3Bsb3QoZXJyLnN2bS5kaXN0KSArCiAgZ2VvbV9jb2wobWFwcGluZyA9IGFlcyh4ID0gc291cmNlLCB5ID0gdHlwMSwgZmlsbCA9IHNvdXJjZSkpICsKICBnZW9tX2Vycm9yYmFyKG1hcHBpbmcgPSBhZXMoeCA9IHNvdXJjZSwgeW1pbiA9IHR5cDEgLSB0eXAxLmVyciwgeW1heCA9IHR5cDEgKyB0eXAxLmVyciksIHdpZHRoID0gMC41KSArCiAgZmFjZXRfd3JhcCh+bWV0aG9kLCBucm93ID0gMSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKGxhYmVscyA9IGMoJzBwYXJldCcgPSAnU3RhcnRpbmcgRGF0YXNldCcsICcxYWRhcHQnID0gJysgQWRhcHRpdmUgU2FtcGxpbmcnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJzJyYW5kbycgPSAnKyBSYW5kb20gU2FtcGxpbmcnKSwgbmFtZSA9ICcnLAogICAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gYygnMHBhcmV0JyA9ICdza3libHVlMicsICcxYWRhcHQnID0gJ3JlZCcsICcycmFuZG8nID0gJ2dyZWVuJykpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSBjKDAsIDAuMDUpKSkgKyBzY2FsZV94X2Rpc2NyZXRlKGJyZWFrcyA9ICcnKSArCiAgdGhlbWVfYncoKSArIGxhYnMoeCA9ICcnLCB5ID0gJ0ZhbHNlIFBvc2l0aXZlXG5FcnJvciBSYXRlJykKCmcudHlwMiA9IGdncGxvdChlcnIuc3ZtLmRpc3QpICsKICBnZW9tX2NvbChtYXBwaW5nID0gYWVzKHggPSBzb3VyY2UsIHkgPSB0eXAyLCBmaWxsID0gc291cmNlKSkgKwogIGdlb21fZXJyb3JiYXIobWFwcGluZyA9IGFlcyh4ID0gc291cmNlLCB5bWluID0gdHlwMiAtIHR5cDIuZXJyLCB5bWF4ID0gdHlwMiArIHR5cDIuZXJyKSwgd2lkdGggPSAwLjUpICsKICBmYWNldF93cmFwKH5tZXRob2QsIG5yb3cgPSAxKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobGFiZWxzID0gYygnMHBhcmV0JyA9ICdTdGFydGluZyBEYXRhc2V0JywgJzFhZGFwdCcgPSAnKyBBZGFwdGl2ZSBTYW1wbGluZycsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnMnJhbmRvJyA9ICcrIFJhbmRvbSBTYW1wbGluZycpLCBuYW1lID0gJycsCiAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBjKCcwcGFyZXQnID0gJ3NreWJsdWUyJywgJzFhZGFwdCcgPSAncmVkJywgJzJyYW5kbycgPSAnZ3JlZW4nKSkgKwogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBleHBhbnNpb24obXVsdCA9IGMoMCwgMC4wNSkpKSArIHNjYWxlX3hfZGlzY3JldGUoYnJlYWtzID0gJycpICsKICB0aGVtZV9idygpICsgbGFicyh4ID0gJycsIHkgPSAnRmFsc2UgTmVnYXRpdmVcbkVycm9yIFJhdGUnKQoKKGcucmF0ZSArIGd1aWRlcyhmaWxsID0gRkFMU0UpKSAvIGcudHlwMSAvIChnLnR5cDIgKyBndWlkZXMoZmlsbCA9IEZBTFNFKSkKd3JpdGUuY3N2KGVyci5zdm0uZGlzdCwgJ1NWTV9lcnJvcl9kZWx0YS5jc3YnKQpybShlcnIucGFyZXQsIGVyci5hZGFwdCwgZXJyLnJhbmRvKQoKYGBgCgpHcmFwaGljYWxseSwgdGhlIHNlbGVjdGlvbiBvZiB0aGUga2VybmVsIGZvcm0gaGFzIGEgbGFyZ2UgaW1wYWN0IG9uIHRoZSBxdWFsaXR5IG9mIHRoZSByZXN1bHQuIApUaGUgcG9seW5vbWlhbCBhbmQgcmFkaWFsIGtlcm5lbHMgYXBwZWFyIHRvIHJvdWdobHkgbWF0Y2ggdGhlIHNoYXBlIG9mIHRoZSBub3JtYWxpemVkIGRpc3RhbmNlID0gMSBjcml0ZXJpb247IAp0aGUgbGluZWFyIGFuZCBzaWdtb2lkYWwga2VybmVscyBkbyBub3QgYXBwZWFyIHRvIG1hdGNoIGFueXRoaW5nLiAKRnVydGhlciB0dW5pbmcgb2YgdGhlIHBvbHlub21pYWwgZGVncmVlIG9yIGNvZWZmaWNpZW50IGNvdWxkIGltcHJvdmUgdGhlIHJlc3VsdHMsIGJ1dCB3aXRob3V0IGEgdHJhaW5pbmctdGVzdGluZyBzZXQgZGVsaW5lYXRpb24sIHRoZXJlIGlzIG5vIHdheSB0byB2YWxpZGF0ZSB0aGUgcmVzdWx0cy4KCgpgYGB7ciBTVk06IERpc3RhbmNlIG1hcmdpbmFsc30Kc3ZtLm1hcmdpbi5wYXJldCA9IG1hcmdpbmFsKG1vZCA9IG1vZC5wYXJldCk7IHN2bS5tYXJnaW4ucGFyZXQkY2F0ID0gJ3N0YXJ0Jwpzdm0ubWFyZ2luLmFkYXB0ID0gbWFyZ2luYWwobW9kID0gbW9kLmFkYXB0KTsgc3ZtLm1hcmdpbi5hZGFwdCRjYXQgPSAnYWRhcHQnCnN2bS5tYXJnaW4ucmFuZG8gPSBtYXJnaW5hbChtb2QgPSBtb2QucmFuZG8pOyBzdm0ubWFyZ2luLnJhbmRvJGNhdCA9ICdyYW5kbycKc3ZtLm1hcmdpbiA9IHJiaW5kKHN2bS5tYXJnaW4ucGFyZXQsIHN2bS5tYXJnaW4uYWRhcHQsIHN2bS5tYXJnaW4ucmFuZG8pCnJtKHN2bS5tYXJnaW4ucGFyZXQsIHN2bS5tYXJnaW4uYWRhcHQsIHN2bS5tYXJnaW4ucmFuZG8pCgp3cml0ZS5jc3Yoc3ZtLm1hcmdpbiwgJ01hcmdpbl9TVk1fZGlzdC5jc3YnKQoKSW5mZXIucGx0ID0gcmVhZC5jc3YoJy4uL0V4X1F1YXJ0aWMvTWFyZ2luYWxzX2RlbHRhLmNzdicpCkluZmVyLnBsdCA9IEluZmVyLnBsdFssIW5hbWVzKEluZmVyLnBsdCkgJWluJSBjKCdYJyldCiMgbmFtZXMoSW5mZXIucGx0KQpJbmZlci5wbHQkbWV0aG9kID0gJ0dQJwoKZ2dwbG90KCkgKwogIGdlb21fcGF0aChkYXRhID0gZmlsdGVyKEluZmVyLnBsdCwgY2F0ID09ICd0cnUnKSwgbWFwcGluZyA9IGFlcyh4ID0geCwgeSA9IHByb2IgLSBwc2QsIGNvbG9yID0gY2F0KSwgbGluZXR5cGUgPSAzKSArCiAgZ2VvbV9wYXRoKGRhdGEgPSBmaWx0ZXIoSW5mZXIucGx0LCBjYXQgPT0gJ3RydScpLCBtYXBwaW5nID0gYWVzKHggPSB4LCB5ID0gcHJvYiArIHBzZCwgY29sb3IgPSBjYXQpLCBsaW5ldHlwZSA9IDMpICsKICBnZW9tX3BhdGgoZGF0YSA9IHN2bS5tYXJnaW4sIG1hcHBpbmcgPSBhZXMoeCA9IHgsIHkgPSBwcm9iLCBsaW5ldHlwZSA9IG1ldGhvZCwgY29sb3IgPSBjYXQpKSArCiAgZmFjZXRfd3JhcCh+dmFyLCBucm93ID0gMikgKyB0aGVtZV9idygpICsKICBsYWJzKHggPSAnSW5wdXQgVmFsdWUnLCB5ID0gJ0NvbmRpdGlvbmFsIFByb2JhYmlsaXR5JywgbGluZXR5cGUgPSAnU1ZNIEtlcm5lbCcsIGNvbG9yID0gJycpICsKICBzY2FsZV9jb2xvcl9tYW51YWwobGFiZWxzID0gYygndHJ1JyA9ICdFeHBlY3RlZCBNYXJnaW5hbCcsICdzdGFydCcgPSAnU3RhcnRpbmcgRGF0YXNldCcsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2FkYXB0JyA9ICcrIEFkYXB0aXZlIFNhbXBsaW5nJywgJ3JhbmRvJyA9ICcrIFJhbmRvbSBTYW1wbGluZycpLAogICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBjKCd0cnUnID0gJ2JsYWNrJywgJ3N0YXJ0JyA9ICdza3libHVlMicsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2FkYXB0JyA9ICdyZWQnLCAncmFuZG8nID0gJ2dyZWVuJyksCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoJ3RydScsICdzdGFydCcsICdhZGFwdCcsICdyYW5kbycpKSArCiAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3QobGluZXR5cGUgPSBjKDMsIDEsIDEsIDEpKSkpICsKICBzY2FsZV9saW5ldHlwZV9kaXNjcmV0ZShsYWJlbHMgPSBjKCdTVk0tcG9sJyA9ICdQb2x5bm9taWFsJywgJ3JhZCcgPSAnU1ZNLVJhZGlhbCcpKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCkpICsgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwgMSkpCgpnZ3Bsb3QoKSArCiAgZ2VvbV9wYXRoKGRhdGEgPSBmaWx0ZXIoSW5mZXIucGx0LCBjYXQgPT0gJ3RydScpLCBtYXBwaW5nID0gYWVzKHggPSB4LCB5ID0gcHJvYiAtIHBzZCwgY29sb3IgPSBjYXQpLCBsaW5ldHlwZSA9IDMpICsKICBnZW9tX3BhdGgoZGF0YSA9IGZpbHRlcihJbmZlci5wbHQsIGNhdCA9PSAndHJ1JyksIG1hcHBpbmcgPSBhZXMoeCA9IHgsIHkgPSBwcm9iICsgcHNkLCBjb2xvciA9IGNhdCksIGxpbmV0eXBlID0gMykgKwogIGdlb21fcGF0aChkYXRhID0gZmlsdGVyKEluZmVyLnBsdCwgY2F0ICE9ICd0cnUnKSwgbWFwcGluZyA9IGFlcyh4ID0geCwgeSA9IHByb2IsIGNvbG9yID0gY2F0KSkgKwogIGZhY2V0X3dyYXAodmFyfi4sIG5yb3cgPSAyKSArIAogIHNjYWxlX2NvbG9yX21hbnVhbChsYWJlbHMgPSBjKCd0cnUnID0gJ0V4cGVjdGVkIE1hcmdpbmFsJywgJ3N0YXJ0JyA9ICdTdGFydGluZyBEYXRhc2V0JywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnYWRhcHQnID0gJysgQWRhcHRpdmUgU2FtcGxpbmcnLCAncmFuZG8nID0gJysgUmFuZG9tIFNhbXBsaW5nJyksCiAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGMoJ3RydScgPSAnYmxhY2snLCAnc3RhcnQnID0gJ3NreWJsdWUyJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnYWRhcHQnID0gJ3JlZCcsICdyYW5kbycgPSAnZ3JlZW4nKSwKICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYygndHJ1JywgJ3N0YXJ0JywgJ2FkYXB0JywgJ3JhbmRvJykpICsKICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChsaW5ldHlwZSA9IGMoMywgMSwgMSwgMSkpKSkgKwogIGxhYnMoeCA9ICcnLCB5ID0gJ1Byb2JhYmlsaXR5IG9mIEFjY2VwdGFuY2UnLCBzdWJ0aXRsZSA9IGV4cHJlc3Npb24oJ1BhcmV0byBEaXN0YW5jZScpLCBjb2xvciA9ICcnKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMC4wNSkpICsgc2NhbGVfeF9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCkpICsKICB0aGVtZV9idygpCgojIENhbGN1bGF0ZSBjb2VmZmljaWVudHMgb2YgZGV0ZXJtaW5hdGlvbjogZWFzaWVyIGNvbXBhcmlzb24KY29lZmRldCA9IGRhdGEuZnJhbWUobWV0aG9kID0gYyhyZXAoJ1NWTS1wb2wnLCAzKSwgcmVwKCdTVk0tcmFkJywgMyksIHJlcCgnR1AnLCAzKSksCiAgICAgICAgICAgZGF0YXNldCA9IGMoJzBzdGFydCcsICcxYWRhcHQnLCAnMnJhbmRvJyksCiBjb2VmID0gYyhjb3IoeCA9IGZpbHRlcihzdm0ubWFyZ2luLCBtZXRob2QgPT0gJ1NWTS1wb2wnLCBjYXQgPT0gJ3N0YXJ0JykkcHJvYiwgCiAgICB5ID0gZmlsdGVyKEluZmVyLnBsdCwgY2F0ID09ICd0cnUnKSRwcm9iLCBtZXRob2QgPSAncGVhcnNvbicpXjIsCiAgY29yKHggPSBmaWx0ZXIoc3ZtLm1hcmdpbiwgbWV0aG9kID09ICdTVk0tcG9sJywgY2F0ID09ICdhZGFwdCcpJHByb2IsIAogICAgeSA9IGZpbHRlcihJbmZlci5wbHQsIGNhdCA9PSAndHJ1JykkcHJvYiwgbWV0aG9kID0gJ3BlYXJzb24nKV4yLAogIGNvcih4ID0gZmlsdGVyKHN2bS5tYXJnaW4sIG1ldGhvZCA9PSAnU1ZNLXBvbCcsIGNhdCA9PSAncmFuZG8nKSRwcm9iLCAKICAgIHkgPSBmaWx0ZXIoSW5mZXIucGx0LCBjYXQgPT0gJ3RydScpJHByb2IsIG1ldGhvZCA9ICdwZWFyc29uJyleMiwKICBjb3IoeCA9IGZpbHRlcihzdm0ubWFyZ2luLCBtZXRob2QgPT0gJ1NWTS1yYWQnLCBjYXQgPT0gJ3N0YXJ0JykkcHJvYiwgCiAgICB5ID0gZmlsdGVyKEluZmVyLnBsdCwgY2F0ID09ICd0cnUnKSRwcm9iLCBtZXRob2QgPSAncGVhcnNvbicpXjIsCiAgY29yKHggPSBmaWx0ZXIoc3ZtLm1hcmdpbiwgbWV0aG9kID09ICdTVk0tcmFkJywgY2F0ID09ICdhZGFwdCcpJHByb2IsIAogICAgeSA9IGZpbHRlcihJbmZlci5wbHQsIGNhdCA9PSAndHJ1JykkcHJvYiwgbWV0aG9kID0gJ3BlYXJzb24nKV4yLAogIGNvcih4ID0gZmlsdGVyKHN2bS5tYXJnaW4sIG1ldGhvZCA9PSAnU1ZNLXJhZCcsIGNhdCA9PSAncmFuZG8nKSRwcm9iLCAKICAgIHkgPSBmaWx0ZXIoSW5mZXIucGx0LCBjYXQgPT0gJ3RydScpJHByb2IsIG1ldGhvZCA9ICdwZWFyc29uJyleMiwKICBjb3IoeCA9IGZpbHRlcihJbmZlci5wbHQsIGNhdCA9PSAnc3RhcnQnKSRwcm9iLCAKICAgIHkgPSBjKGZpbHRlcihJbmZlci5wbHQsIGNhdCA9PSAndHJ1JywgdmFyID09ICd4MScpJHByb2IsIGZpbHRlcihJbmZlci5wbHQsIGNhdCA9PSAndHJ1JywgdmFyID09ICd4MicpJHByb2IpLCAKICAgIG1ldGhvZCA9ICdwZWFyc29uJyleMiwKICBjb3IoeCA9IGZpbHRlcihJbmZlci5wbHQsIGNhdCA9PSAnYWRhcHQnKSRwcm9iLCAKICAgIHkgPSBjKGZpbHRlcihJbmZlci5wbHQsIGNhdCA9PSAndHJ1JywgdmFyID09ICd4MScpJHByb2IsIGZpbHRlcihJbmZlci5wbHQsIGNhdCA9PSAndHJ1JywgdmFyID09ICd4MicpJHByb2IpLCAKICAgIG1ldGhvZCA9ICdwZWFyc29uJyleMiwKICBjb3IoeCA9IGZpbHRlcihJbmZlci5wbHQsIGNhdCA9PSAncmFuZG8nKSRwcm9iLCAKICAgIHkgPSBjKGZpbHRlcihJbmZlci5wbHQsIGNhdCA9PSAndHJ1JywgdmFyID09ICd4MScpJHByb2IsIGZpbHRlcihJbmZlci5wbHQsIGNhdCA9PSAndHJ1JywgdmFyID09ICd4MicpJHByb2IpLCAKICAgIG1ldGhvZCA9ICdwZWFyc29uJyleMikpCgpjb2VmZGV0CndyaXRlLmNzdihjb2VmZGV0LCAnTWFyZ2luYWxzX0NvZWZEZXRfZGVsdGEuY3N2JykKCmdncGxvdChjb2VmZGV0KSArCiAgZ2VvbV9jb2wobWFwcGluZyA9IGFlcyh4ID0gZGF0YXNldCwgZmlsbCA9IGRhdGFzZXQsIHkgPSBjb2VmKSkrCiAgZmFjZXRfZ3JpZCh+bWV0aG9kKSArCiAgbGFicyh4ID0gJycsIHkgPSAnMS1WYXJpYWJsZSBNYXJnaW5hbCBDb2VmZmljaWVudCBvZiBEZXRlcm1pbmF0aW9uJywgc3VidGl0bGUgPSAnUGFyZXRvIERpc3RhbmNlJykgKwogIHNjYWxlX3hfZGlzY3JldGUoYnJlYWtzID0gYygpKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobGFiZWxzID0gYygnMHN0YXJ0JyA9ICdTdGFydGluZyBEYXRhc2V0JywgJzFhZGFwdCcgPSAnKyBBZGFwdGl2ZSBTYW1wbGluZycsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnMnJhbmRvJyA9ICcrIFJhbmRvbSBTYW1wbGluZycpLCBuYW1lID0gJycsCiAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gYygnMHN0YXJ0JyA9ICdza3libHVlMicsICcxYWRhcHQnID0gJ3JlZCcsICcycmFuZG8nID0gJ2dyZWVuJykpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSBjKDAsIDAuMDUpKSkKCmBgYAoKU2luZ2xlIHZhcmlhYmxlIG1hcmdpbmFscyBhcmUgb2sgdmlzdWFsbHksIGJ1dCBjYWxjdWxhdGluZyB0aGUgY29lZmZpY2llbnQgb2YgZGV0ZXJtaW5hdGlvbiBzaG93cyB0aGF0IHRoZSBHUCBtZXRob2QgaXMgbW9yZSBjb25zaXN0ZW50IHJlZ2FyZGxlc3Mgb2YgZGF0YXNldCBhbmQgaXQgZGVtb25zdHJhdGVzIHRoZSBleHBlY3RlZCBpbXByb3ZlbWVudCB3aXRoIGluY3JlYXNlZCBzYW1wbGluZyBuZWFyIHRoZSBib3VuZGFyeS4KVGhlIFNWTSBtZXRob2RzIG92ZXJhbGwgY2Fubm90IGNhcHR1cmUgdGhlIGJvdW5kYXJ5IHZlcnkgd2VsbCwgbGVhZGluZyB0byBjbGFzc2lmaWNhdGlvbiBlcnJvcnMuCgoKYGBge3IgU1ZNOiBUaHJlc2hvbGR9CmRhdGEucGFyZXQgPSByZWFkLmNzdignLi4vRXhfUXVhcnRpYy9HUGFyX2FsbF9zdGFydC5jc3YnKQpkYXRhLmN1dG9mID0gcmVhZC5jc3YoJy4uL0V4X1F1YXJ0aWMvR1Bhcl9BY2NlcHRfVGhyZXNob2xkLmNzdicpCmRhdGEucmFuZG8gPSByZWFkLmNzdihmaWxlID0gJ0dQYXJfUmFuZG9tLmNzdicpCgojIERlZmluZSBhY2NlcHRhbmNlCmRhdGEucGFyZXQkY2F0ID0gMDsgZGF0YS5jdXRvZiRjYXQgPSAwOyBkYXRhLnJhbmRvJGNhdCA9IDA7IGZpbmUuZ3JpZCRjYXQgPSAwCmRhdGEucGFyZXQkY2F0W2RhdGEucGFyZXQkZjEubm9ybSA8PSAxICYgZGF0YS5wYXJldCRmMi5ub3JtIDw9IDFdID0gMQpkYXRhLmN1dG9mJGNhdFtkYXRhLmN1dG9mJGYxLm5vcm0gPD0gMSAmIGRhdGEuY3V0b2YkZjIubm9ybSA8PSAxXSA9IDEKZGF0YS5yYW5kbyRjYXRbZGF0YS5yYW5kbyRmMS5ub3JtIDw9IDEgJiBkYXRhLnJhbmRvJGYyLm5vcm0gPD0gMV0gPSAxCmZpbmUuZ3JpZCRjYXRbZmluZS5ncmlkJGYxLm5vcm0gPD0gMSAmIGZpbmUuZ3JpZCRmMi5ub3JtIDw9IDFdID0gMQoKIyBQbG90cwptb2QucGFyZXQgPSBTVk0ubW9kLmFsbChpbnB1dC5kYXRhID0gZGF0YS5wYXJldCkKU1ZNLmlucHV0KGZpbmUuaW5wdXQgPSBmaW5lLmdyaWRbLGMoJ3gxJywgJ3gyJywgJ2NhdCcpXSwgCiAgICAgICAgICBtb2QucmFkID0gbW9kLnBhcmV0JHJhZCwgbW9kLmxpbiA9IG1vZC5wYXJldCRsaW4sIAogICAgICAgICAgbW9kLnBvbCA9IG1vZC5wYXJldCRwb2wsIG1vZC5zaWcgPSBtb2QucGFyZXQkc2lnLAogICAgICAgICAgdGl0ID0gJ1RocmVzaG9sZCBDdXRvZmYsIFN0YXJ0aW5nIERhdGFzZXQnKQoKbW9kLmFkYXB0ID0gU1ZNLm1vZC5hbGwoaW5wdXQuZGF0YSA9IGRhdGEuY3V0b2YpCm1vZC5hZGFwdC5jdXRvZiA9IG1vZC5hZGFwdApTVk0uaW5wdXQoZmluZS5pbnB1dCA9IGZpbmUuZ3JpZFssYygneDEnLCAneDInLCAnY2F0JyldLCAKICAgICAgICAgIG1vZC5yYWQgPSBtb2QuYWRhcHQkcmFkLCBtb2QubGluID0gbW9kLmFkYXB0JGxpbiwgCiAgICAgICAgICBtb2QucG9sID0gbW9kLmFkYXB0JHBvbCwgbW9kLnNpZyA9IG1vZC5hZGFwdCRzaWcsCiAgICAgICAgICB0aXQgPSAnVGhyZXNob2xkIEN1dG9mZiwgKyBBZGFwdGl2ZSBTYW1wbGluZycpCgptb2QucmFuZG8gPSBTVk0ubW9kLmFsbChpbnB1dC5kYXRhID0gZGF0YS5yYW5kbykKU1ZNLmlucHV0KGZpbmUuaW5wdXQgPSBmaW5lLmdyaWRbLGMoJ3gxJywgJ3gyJywgJ2NhdCcpXSwgCiAgICAgICAgICBtb2QucmFkID0gbW9kLnJhbmRvJHJhZCwgbW9kLmxpbiA9IG1vZC5yYW5kbyRsaW4sIAogICAgICAgICAgbW9kLnBvbCA9IG1vZC5yYW5kbyRwb2wsIG1vZC5zaWcgPSBtb2QucmFuZG8kc2lnLAogICAgICAgICAgdGl0ID0gJ1RocmVzaG9sZCBDdXRvZmYsICsgUmFuZG9tIFNhbXBsaW5nJykKCiMgRXJyb3IgcmF0ZXMKZXJyLnBhcmV0ID0gU1ZNLmVycihmaW5lLmlucHV0ID0gZmluZS5ncmlkLCBtb2QubGlzdCA9IG1vZC5wYXJldCkKZXJyLnBhcmV0JHNvdXJjZSA9ICcwcGFyZXQnOyBlcnIucGFyZXQkY3JpdGVyaWEgPSAnVHJlc2hvbGQgQ3V0b2ZmJwplcnIuYWRhcHQgPSBTVk0uZXJyKGZpbmUuaW5wdXQgPSBmaW5lLmdyaWQsIG1vZC5saXN0ID0gbW9kLmFkYXB0KQplcnIuYWRhcHQkc291cmNlID0gJzFhZGFwdCc7IGVyci5hZGFwdCRjcml0ZXJpYSA9ICdUcmVzaG9sZCBDdXRvZmYnCmVyci5yYW5kbyA9IFNWTS5lcnIoZmluZS5pbnB1dCA9IGZpbmUuZ3JpZCwgbW9kLmxpc3QgPSBtb2QucmFuZG8pCmVyci5yYW5kbyRzb3VyY2UgPSAnMnJhbmRvJzsgZXJyLnJhbmRvJGNyaXRlcmlhID0gJ1RyZXNob2xkIEN1dG9mZicKCmVyci5zdm0uY3V0b2YgPSByYmluZChlcnIucGFyZXQsIGVyci5hZGFwdCwgZXJyLnJhbmRvKQoKZy5yYXRlID0gZ2dwbG90KGVyci5zdm0uY3V0b2YpICsKICBnZW9tX2NvbChtYXBwaW5nID0gYWVzKHggPSBzb3VyY2UsIHkgPSByYXRlLCBmaWxsID0gc291cmNlKSkgKwogIGdlb21fZXJyb3JiYXIobWFwcGluZyA9IGFlcyh4ID0gc291cmNlLCB5bWluID0gcmF0ZSAtIGVyciwgeW1heCA9IHJhdGUgKyBlcnIpLCB3aWR0aCA9IDAuNSkgKwogIGZhY2V0X3dyYXAofm1ldGhvZCwgbnJvdyA9IDEpICsKICBzY2FsZV9maWxsX21hbnVhbChsYWJlbHMgPSBjKCcwcGFyZXQnID0gJ1N0YXJ0aW5nIERhdGFzZXQnLCAnMWFkYXB0JyA9ICcrIEFkYXB0aXZlIFNhbXBsaW5nJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICcycmFuZG8nID0gJysgUmFuZG9tIFNhbXBsaW5nJyksIG5hbWUgPSAnJywKICAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGMoJzBwYXJldCcgPSAnc2t5Ymx1ZTInLCAnMWFkYXB0JyA9ICdyZWQnLCAnMnJhbmRvJyA9ICdncmVlbicpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gYygwLCAwLjA1KSkpICsgc2NhbGVfeF9kaXNjcmV0ZShicmVha3MgPSAnJykgKwogIHRoZW1lX2J3KCkgKyBsYWJzKHggPSAnJywgeSA9ICdUb3RhbFxuRXJyb3IgUmF0ZScsIHN1YnRpdGxlID0gJ0N1dG9mZiBUaHJlc2hvbGQnKQoKZy50eXAxID0gZ2dwbG90KGVyci5zdm0uY3V0b2YpICsKICBnZW9tX2NvbChtYXBwaW5nID0gYWVzKHggPSBzb3VyY2UsIHkgPSB0eXAxLCBmaWxsID0gc291cmNlKSkgKwogIGdlb21fZXJyb3JiYXIobWFwcGluZyA9IGFlcyh4ID0gc291cmNlLCB5bWluID0gdHlwMSAtIHR5cDEuZXJyLCB5bWF4ID0gdHlwMSArIHR5cDEuZXJyKSwgd2lkdGggPSAwLjUpICsKICBmYWNldF93cmFwKH5tZXRob2QsIG5yb3cgPSAxKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobGFiZWxzID0gYygnMHBhcmV0JyA9ICdTdGFydGluZyBEYXRhc2V0JywgJzFhZGFwdCcgPSAnKyBBZGFwdGl2ZSBTYW1wbGluZycsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnMnJhbmRvJyA9ICcrIFJhbmRvbSBTYW1wbGluZycpLCBuYW1lID0gJycsCiAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBjKCcwcGFyZXQnID0gJ3NreWJsdWUyJywgJzFhZGFwdCcgPSAncmVkJywgJzJyYW5kbycgPSAnZ3JlZW4nKSkgKwogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBleHBhbnNpb24obXVsdCA9IGMoMCwgMC4wNSkpKSArIHNjYWxlX3hfZGlzY3JldGUoYnJlYWtzID0gJycpICsKICB0aGVtZV9idygpICsgbGFicyh4ID0gJycsIHkgPSAnRmFsc2UgUG9zaXRpdmVcbkVycm9yIFJhdGUnKQoKZy50eXAyID0gZ2dwbG90KGVyci5zdm0uY3V0b2YpICsKICBnZW9tX2NvbChtYXBwaW5nID0gYWVzKHggPSBzb3VyY2UsIHkgPSB0eXAyLCBmaWxsID0gc291cmNlKSkgKwogIGdlb21fZXJyb3JiYXIobWFwcGluZyA9IGFlcyh4ID0gc291cmNlLCB5bWluID0gdHlwMiAtIHR5cDIuZXJyLCB5bWF4ID0gdHlwMiArIHR5cDIuZXJyKSwgd2lkdGggPSAwLjUpICsKICBmYWNldF93cmFwKH5tZXRob2QsIG5yb3cgPSAxKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobGFiZWxzID0gYygnMHBhcmV0JyA9ICdTdGFydGluZyBEYXRhc2V0JywgJzFhZGFwdCcgPSAnKyBBZGFwdGl2ZSBTYW1wbGluZycsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnMnJhbmRvJyA9ICcrIFJhbmRvbSBTYW1wbGluZycpLCBuYW1lID0gJycsCiAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBjKCcwcGFyZXQnID0gJ3NreWJsdWUyJywgJzFhZGFwdCcgPSAncmVkJywgJzJyYW5kbycgPSAnZ3JlZW4nKSkgKwogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBleHBhbnNpb24obXVsdCA9IGMoMCwgMC4wNSkpKSArIHNjYWxlX3hfZGlzY3JldGUoYnJlYWtzID0gJycpICsKICB0aGVtZV9idygpICsgbGFicyh4ID0gJycsIHkgPSAnRmFsc2UgTmVnYXRpdmVcbkVycm9yIFJhdGUnKQoKKGcucmF0ZSArIGd1aWRlcyhmaWxsID0gRkFMU0UpKSAvIGcudHlwMSAvIChnLnR5cDIgKyBndWlkZXMoZmlsbCA9IEZBTFNFKSkKd3JpdGUuY3N2KGVyci5zdm0uY3V0b2YsICdTVk1fZXJyb3JfY3V0b2YuY3N2JykKCnJtKGVyci5wYXJldCwgZXJyLmFkYXB0LCBlcnIucmFuZG8sIGcucmF0ZSwgZy50eXAxLCBnLnR5cDIpCgpgYGAKCmBgYHtyIFNWTSBNYXJnaW5hbGl6YXRpb246IFRocmVzaG9sZCBDdXRvZmZ9CnN2bS5tYXJnaW4ucGFyZXQgPSBtYXJnaW5hbChtb2QgPSBtb2QucGFyZXQpOyBzdm0ubWFyZ2luLnBhcmV0JGNhdCA9ICdzdGFydCcKc3ZtLm1hcmdpbi5hZGFwdCA9IG1hcmdpbmFsKG1vZCA9IG1vZC5hZGFwdCk7IHN2bS5tYXJnaW4uYWRhcHQkY2F0ID0gJ2FkYXB0Jwpzdm0ubWFyZ2luLnJhbmRvID0gbWFyZ2luYWwobW9kID0gbW9kLnJhbmRvKTsgc3ZtLm1hcmdpbi5yYW5kbyRjYXQgPSAncmFuZG8nCnN2bS5tYXJnaW4gPSByYmluZChzdm0ubWFyZ2luLnBhcmV0LCBzdm0ubWFyZ2luLmFkYXB0LCBzdm0ubWFyZ2luLnJhbmRvKQpybShzdm0ubWFyZ2luLnBhcmV0LCBzdm0ubWFyZ2luLmFkYXB0LCBzdm0ubWFyZ2luLnJhbmRvKQoKd3JpdGUuY3N2KHN2bS5tYXJnaW4sICdNYXJnaW5fU1ZNX2N1dG9mLmNzdicpCgpJbmZlci5wbHQgPSByZWFkLmNzdignLi4vRXhfUXVhcnRpYy9NYXJnaW5hbHNfY3V0b2YuY3N2JykKSW5mZXIucGx0ID0gSW5mZXIucGx0WywhbmFtZXMoSW5mZXIucGx0KSAlaW4lIGMoJ1gnKV0KIyBuYW1lcyhJbmZlci5wbHQpCkluZmVyLnBsdCRtZXRob2QgPSAnR1AnCgpnZ3Bsb3QoKSArCiAgZ2VvbV9wYXRoKGRhdGEgPSBmaWx0ZXIoSW5mZXIucGx0LCBjYXQgPT0gJ3RydScpLCBtYXBwaW5nID0gYWVzKHggPSB4LCB5ID0gcHJvYiAtIHBzZCwgY29sb3IgPSBjYXQpLCBsaW5ldHlwZSA9IDMpICsKICBnZW9tX3BhdGgoZGF0YSA9IGZpbHRlcihJbmZlci5wbHQsIGNhdCA9PSAndHJ1JyksIG1hcHBpbmcgPSBhZXMoeCA9IHgsIHkgPSBwcm9iICsgcHNkLCBjb2xvciA9IGNhdCksIGxpbmV0eXBlID0gMykgKwogIGdlb21fcGF0aChkYXRhID0gc3ZtLm1hcmdpbiwgbWFwcGluZyA9IGFlcyh4ID0geCwgeSA9IHByb2IsIGxpbmV0eXBlID0gbWV0aG9kLCBjb2xvciA9IGNhdCkpICsKICBmYWNldF93cmFwKH52YXIsIG5yb3cgPSAyKSArIHRoZW1lX2J3KCkgKwogIGxhYnMoeCA9ICdJbnB1dCBWYWx1ZScsIHkgPSAnQ29uZGl0aW9uYWwgUHJvYmFiaWxpdHknLCBsaW5ldHlwZSA9ICdTVk0gS2VybmVsJywgY29sb3IgPSAnJywgc3VidGl0bGUgPSAnQ3V0b2ZmIFRocmVzaG9sZCcpICsKICBzY2FsZV9jb2xvcl9tYW51YWwobGFiZWxzID0gYygndHJ1JyA9ICdFeHBlY3RlZCBNYXJnaW5hbCcsICdzdGFydCcgPSAnU3RhcnRpbmcgRGF0YXNldCcsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2FkYXB0JyA9ICcrIEFkYXB0aXZlIFNhbXBsaW5nJywgJ3JhbmRvJyA9ICcrIFJhbmRvbSBTYW1wbGluZycpLAogICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBjKCd0cnUnID0gJ2JsYWNrJywgJ3N0YXJ0JyA9ICdza3libHVlMicsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2FkYXB0JyA9ICdyZWQnLCAncmFuZG8nID0gJ2dyZWVuJyksCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoJ3RydScsICdzdGFydCcsICdhZGFwdCcsICdyYW5kbycpKSArCiAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3QobGluZXR5cGUgPSBjKDMsIDEsIDEsIDEpKSkpICsKICBzY2FsZV9saW5ldHlwZV9kaXNjcmV0ZShsYWJlbHMgPSBjKCdTVk0tcG9sJyA9ICdQb2x5bm9taWFsJywgJ3JhZCcgPSAnU1ZNLVJhZGlhbCcpKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCkpICsgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwgMSkpCgpnZ3Bsb3QoKSArCiAgZ2VvbV9wYXRoKGRhdGEgPSBmaWx0ZXIoSW5mZXIucGx0LCBjYXQgPT0gJ3RydScpLCBtYXBwaW5nID0gYWVzKHggPSB4LCB5ID0gcHJvYiAtIHBzZCwgY29sb3IgPSBjYXQpLCBsaW5ldHlwZSA9IDMpICsKICBnZW9tX3BhdGgoZGF0YSA9IGZpbHRlcihJbmZlci5wbHQsIGNhdCA9PSAndHJ1JyksIG1hcHBpbmcgPSBhZXMoeCA9IHgsIHkgPSBwcm9iICsgcHNkLCBjb2xvciA9IGNhdCksIGxpbmV0eXBlID0gMykgKwogIGdlb21fcGF0aChkYXRhID0gZmlsdGVyKEluZmVyLnBsdCwgY2F0ICE9ICd0cnUnKSwgbWFwcGluZyA9IGFlcyh4ID0geCwgeSA9IHByb2IsIGNvbG9yID0gY2F0KSkgKwogIGZhY2V0X3dyYXAodmFyfi4sIG5yb3cgPSAyKSArIAogIHNjYWxlX2NvbG9yX21hbnVhbChsYWJlbHMgPSBjKCd0cnUnID0gJ0V4cGVjdGVkIE1hcmdpbmFsJywgJ3N0YXJ0JyA9ICdTdGFydGluZyBEYXRhc2V0JywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnYWRhcHQnID0gJysgQWRhcHRpdmUgU2FtcGxpbmcnLCAncmFuZG8nID0gJysgUmFuZG9tIFNhbXBsaW5nJyksCiAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGMoJ3RydScgPSAnYmxhY2snLCAnc3RhcnQnID0gJ3NreWJsdWUyJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnYWRhcHQnID0gJ3JlZCcsICdyYW5kbycgPSAnZ3JlZW4nKSwKICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYygndHJ1JywgJ3N0YXJ0JywgJ2FkYXB0JywgJ3JhbmRvJykpICsKICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChsaW5ldHlwZSA9IGMoMywgMSwgMSwgMSkpKSkgKwogIGxhYnMoeCA9ICcnLCB5ID0gJ1Byb2JhYmlsaXR5IG9mIEFjY2VwdGFuY2UnLCBzdWJ0aXRsZSA9ICdDdXRvZmYgVGhyZXNob2xkJywgY29sb3IgPSAnJykgKwogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBjKDAsIDAuMDUpKSArIHNjYWxlX3hfY29udGludW91cyhleHBhbmQgPSBjKDAsIDApKSArCiAgdGhlbWVfYncoKQoKIyBDYWxjdWxhdGUgY29lZmZpY2llbnRzIG9mIGRldGVybWluYXRpb246IGVhc2llciBjb21wYXJpc29uCmNvZWZkZXQgPSBkYXRhLmZyYW1lKG1ldGhvZCA9IGMocmVwKCdTVk0tcG9sJywgMyksIHJlcCgnU1ZNLXJhZCcsIDMpLCByZXAoJ0dQJywgMykpLAogICAgICAgICAgIGRhdGFzZXQgPSBjKCcwc3RhcnQnLCAnMWFkYXB0JywgJzJyYW5kbycpLAogY29lZiA9IGMoY29yKHggPSBmaWx0ZXIoc3ZtLm1hcmdpbiwgbWV0aG9kID09ICdTVk0tcG9sJywgY2F0ID09ICdzdGFydCcpJHByb2IsIAogICAgeSA9IGZpbHRlcihJbmZlci5wbHQsIGNhdCA9PSAndHJ1JykkcHJvYiwgbWV0aG9kID0gJ3BlYXJzb24nKV4yLAogIGNvcih4ID0gZmlsdGVyKHN2bS5tYXJnaW4sIG1ldGhvZCA9PSAnU1ZNLXBvbCcsIGNhdCA9PSAnYWRhcHQnKSRwcm9iLCAKICAgIHkgPSBmaWx0ZXIoSW5mZXIucGx0LCBjYXQgPT0gJ3RydScpJHByb2IsIG1ldGhvZCA9ICdwZWFyc29uJyleMiwKICBjb3IoeCA9IGZpbHRlcihzdm0ubWFyZ2luLCBtZXRob2QgPT0gJ1NWTS1wb2wnLCBjYXQgPT0gJ3JhbmRvJykkcHJvYiwgCiAgICB5ID0gZmlsdGVyKEluZmVyLnBsdCwgY2F0ID09ICd0cnUnKSRwcm9iLCBtZXRob2QgPSAncGVhcnNvbicpXjIsCiAgY29yKHggPSBmaWx0ZXIoc3ZtLm1hcmdpbiwgbWV0aG9kID09ICdTVk0tcmFkJywgY2F0ID09ICdzdGFydCcpJHByb2IsIAogICAgeSA9IGZpbHRlcihJbmZlci5wbHQsIGNhdCA9PSAndHJ1JykkcHJvYiwgbWV0aG9kID0gJ3BlYXJzb24nKV4yLAogIGNvcih4ID0gZmlsdGVyKHN2bS5tYXJnaW4sIG1ldGhvZCA9PSAnU1ZNLXJhZCcsIGNhdCA9PSAnYWRhcHQnKSRwcm9iLCAKICAgIHkgPSBmaWx0ZXIoSW5mZXIucGx0LCBjYXQgPT0gJ3RydScpJHByb2IsIG1ldGhvZCA9ICdwZWFyc29uJyleMiwKICBjb3IoeCA9IGZpbHRlcihzdm0ubWFyZ2luLCBtZXRob2QgPT0gJ1NWTS1yYWQnLCBjYXQgPT0gJ3JhbmRvJykkcHJvYiwgCiAgICB5ID0gZmlsdGVyKEluZmVyLnBsdCwgY2F0ID09ICd0cnUnKSRwcm9iLCBtZXRob2QgPSAncGVhcnNvbicpXjIsCiAgY29yKHggPSBmaWx0ZXIoSW5mZXIucGx0LCBjYXQgPT0gJ3N0YXJ0JykkcHJvYiwgCiAgICB5ID0gYyhmaWx0ZXIoSW5mZXIucGx0LCBjYXQgPT0gJ3RydScsIHZhciA9PSAneDEnKSRwcm9iLCBmaWx0ZXIoSW5mZXIucGx0LCBjYXQgPT0gJ3RydScsIHZhciA9PSAneDInKSRwcm9iKSwgCiAgICBtZXRob2QgPSAncGVhcnNvbicpXjIsCiAgY29yKHggPSBmaWx0ZXIoSW5mZXIucGx0LCBjYXQgPT0gJ2FkYXB0JykkcHJvYiwgCiAgICB5ID0gYyhmaWx0ZXIoSW5mZXIucGx0LCBjYXQgPT0gJ3RydScsIHZhciA9PSAneDEnKSRwcm9iLCBmaWx0ZXIoSW5mZXIucGx0LCBjYXQgPT0gJ3RydScsIHZhciA9PSAneDInKSRwcm9iKSwgCiAgICBtZXRob2QgPSAncGVhcnNvbicpXjIsCiAgY29yKHggPSBmaWx0ZXIoSW5mZXIucGx0LCBjYXQgPT0gJ3JhbmRvJykkcHJvYiwgCiAgICB5ID0gYyhmaWx0ZXIoSW5mZXIucGx0LCBjYXQgPT0gJ3RydScsIHZhciA9PSAneDEnKSRwcm9iLCBmaWx0ZXIoSW5mZXIucGx0LCBjYXQgPT0gJ3RydScsIHZhciA9PSAneDInKSRwcm9iKSwgCiAgICBtZXRob2QgPSAncGVhcnNvbicpXjIpKQoKY29lZmRldAp3cml0ZS5jc3YoY29lZmRldCwgJ01hcmdpbmFsc19Db2VmRGV0X2N1dG9mLmNzdicpCgpnZ3Bsb3QoY29lZmRldCkgKwogIGdlb21fY29sKG1hcHBpbmcgPSBhZXMoeCA9IGRhdGFzZXQsIGZpbGwgPSBkYXRhc2V0LCB5ID0gY29lZikpKwogIGZhY2V0X2dyaWQofm1ldGhvZCkgKwogIGxhYnMoeCA9ICcnLCB5ID0gJzEtVmFyaWFibGUgTWFyZ2luYWwgQ29lZmZpY2llbnQgb2YgRGV0ZXJtaW5hdGlvbicsIHN1YnRpdGxlID0gJ0N1dG9mZiBUaHJlc2hvbGQnKSArCiAgc2NhbGVfeF9kaXNjcmV0ZShicmVha3MgPSBjKCkpICsKICBzY2FsZV9maWxsX21hbnVhbChsYWJlbHMgPSBjKCcwc3RhcnQnID0gJ1N0YXJ0aW5nIERhdGFzZXQnLCAnMWFkYXB0JyA9ICcrIEFkYXB0aXZlIFNhbXBsaW5nJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICcycmFuZG8nID0gJysgUmFuZG9tIFNhbXBsaW5nJyksIG5hbWUgPSAnJywKICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBjKCcwc3RhcnQnID0gJ3NreWJsdWUyJywgJzFhZGFwdCcgPSAncmVkJywgJzJyYW5kbycgPSAnZ3JlZW4nKSkgKwogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBleHBhbnNpb24obXVsdCA9IGMoMCwgMC4wNSkpKQoKYGBgCgpSZXBlYXQgZm9yIHJhZGl1cy1hbmdsZSBjdXRvZmYKCmBgYHtyIFNWTTogUmFkaXVzIGFuZCBQcmlvcml0eX0KZGF0YS5wYXJldCA9IHJlYWQuY3N2KGZpbGUgPSAnLi4vRXhfUXVhcnRpYy9HUGFyX2FsbF9zdGFydC5jc3YnKQpkYXRhLnJhZGFuID0gcmVhZC5jc3YoJy4uL0V4X1F1YXJ0aWMvR1Bhcl9BY2NlcHRfUmFkaXVzLmNzdicpCmRhdGEucmFuZG8gPSByZWFkLmNzdihmaWxlID0gJ0dQYXJfUmFuZG9tLmNzdicpCgojIERlZmluZSBhY2NlcHRhbmNlCmRhdGEucGFyZXQkY2F0ID0gMDsgZGF0YS5yYWRhbiRjYXQgPSAwOyBkYXRhLnJhbmRvJGNhdCA9IDA7IGZpbmUuZ3JpZCRjYXQgPSAwCmRhdGEucGFyZXQkY2F0W3NxcnQoZGF0YS5wYXJldCRmMS5ub3JtXjIgKyBkYXRhLnBhcmV0JGYyLm5vcm1eMikgPD0gMSAmIGRhdGEucGFyZXQkdGhldGEgPiAyMF0gPSAxCmRhdGEucmFkYW4kY2F0W3NxcnQoZGF0YS5yYWRhbiRmMS5ub3JtXjIgKyBkYXRhLnJhZGFuJGYyLm5vcm1eMikgPD0gMSAmIGRhdGEucmFkYW4kdGhldGEgPiAyMF0gPSAxCmRhdGEucmFuZG8kY2F0W3NxcnQoZGF0YS5yYW5kbyRmMS5ub3JtXjIgKyBkYXRhLnJhbmRvJGYyLm5vcm1eMikgPD0gMSAmIGRhdGEucmFuZG8kdGhldGEgPiAyMF0gPSAxCmZpbmUuZ3JpZCRjYXRbc3FydChmaW5lLmdyaWQkZjEubm9ybV4yICsgZmluZS5ncmlkJGYyLm5vcm1eMikgPD0gMSAmIGZpbmUuZ3JpZCRhbmcgPiAyMF0gPSAxCgojIFBsb3RzCm1vZC5wYXJldCA9IFNWTS5tb2QuYWxsKGlucHV0LmRhdGEgPSBkYXRhLnBhcmV0KQpTVk0uaW5wdXQoZmluZS5pbnB1dCA9IGZpbmUuZ3JpZFssYygneDEnLCAneDInLCAnY2F0JyldLCAKICAgICAgICAgIG1vZC5yYWQgPSBtb2QucGFyZXQkcmFkLCBtb2QubGluID0gbW9kLnBhcmV0JGxpbiwgCiAgICAgICAgICBtb2QucG9sID0gbW9kLnBhcmV0JHBvbCwgbW9kLnNpZyA9IG1vZC5wYXJldCRzaWcsCiAgICAgICAgICB0aXQgPSAnVXRvcGlhIERpc3RhbmNlICsgUHJpb3JpdHksIFN0YXJ0aW5nIERhdGFzZXQnKQoKbW9kLmFkYXB0ID0gU1ZNLm1vZC5hbGwoaW5wdXQuZGF0YSA9IGRhdGEucmFkYW4pCm1vZC5hZGFwdC5yYWRhbiA9IG1vZC5hZGFwdApTVk0uaW5wdXQoZmluZS5pbnB1dCA9IGZpbmUuZ3JpZFssYygneDEnLCAneDInLCAnY2F0JyldLCAKICAgICAgICAgIG1vZC5yYWQgPSBtb2QuYWRhcHQkcmFkLCBtb2QubGluID0gbW9kLmFkYXB0JGxpbiwgCiAgICAgICAgICBtb2QucG9sID0gbW9kLmFkYXB0JHBvbCwgbW9kLnNpZyA9IG1vZC5hZGFwdCRzaWcsCiAgICAgICAgICB0aXQgPSAnVXRvcGlhIERpc3RhbmNlICsgUHJpb3JpdHksICsgQWRhcHRpdmUgU2FtcGxpbmcnKQoKbW9kLnJhbmRvID0gU1ZNLm1vZC5hbGwoaW5wdXQuZGF0YSA9IGRhdGEucmFuZG8pClNWTS5pbnB1dChmaW5lLmlucHV0ID0gZmluZS5ncmlkWyxjKCd4MScsICd4MicsICdjYXQnKV0sIAogICAgICAgICAgbW9kLnJhZCA9IG1vZC5yYW5kbyRyYWQsIG1vZC5saW4gPSBtb2QucmFuZG8kbGluLCAKICAgICAgICAgIG1vZC5wb2wgPSBtb2QucmFuZG8kcG9sLCBtb2Quc2lnID0gbW9kLnJhbmRvJHNpZywKICAgICAgICAgIHRpdCA9ICdVdG9waWEgRGlzdGFuY2UgKyBQcmlvcml0eSwgKyBSYW5kb20gU2FtcGxpbmcnKQoKIyBFcnJvciByYXRlcwplcnIucGFyZXQgPSBTVk0uZXJyKGZpbmUuaW5wdXQgPSBmaW5lLmdyaWQsIG1vZC5saXN0ID0gbW9kLnBhcmV0KQplcnIucGFyZXQkc291cmNlID0gJzBwYXJldCc7IGVyci5wYXJldCRjcml0ZXJpYSA9ICdUcmVzaG9sZCBDdXRvZmYnCmVyci5hZGFwdCA9IFNWTS5lcnIoZmluZS5pbnB1dCA9IGZpbmUuZ3JpZCwgbW9kLmxpc3QgPSBtb2QuYWRhcHQpCmVyci5hZGFwdCRzb3VyY2UgPSAnMWFkYXB0JzsgZXJyLmFkYXB0JGNyaXRlcmlhID0gJ1RyZXNob2xkIEN1dG9mZicKZXJyLnJhbmRvID0gU1ZNLmVycihmaW5lLmlucHV0ID0gZmluZS5ncmlkLCBtb2QubGlzdCA9IG1vZC5yYW5kbykKZXJyLnJhbmRvJHNvdXJjZSA9ICcycmFuZG8nOyBlcnIucmFuZG8kY3JpdGVyaWEgPSAnVHJlc2hvbGQgQ3V0b2ZmJwoKZXJyLnN2bS5yYWRhbiA9IHJiaW5kKGVyci5wYXJldCwgZXJyLmFkYXB0LCBlcnIucmFuZG8pCgpnLnJhdGUgPSBnZ3Bsb3QoZXJyLnN2bS5yYWRhbikgKwogIGdlb21fY29sKG1hcHBpbmcgPSBhZXMoeCA9IHNvdXJjZSwgeSA9IHJhdGUsIGZpbGwgPSBzb3VyY2UpKSArCiAgZ2VvbV9lcnJvcmJhcihtYXBwaW5nID0gYWVzKHggPSBzb3VyY2UsIHltaW4gPSByYXRlIC0gZXJyLCB5bWF4ID0gcmF0ZSArIGVyciksIHdpZHRoID0gMC41KSArCiAgZmFjZXRfd3JhcCh+bWV0aG9kLCBucm93ID0gMSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKGxhYmVscyA9IGMoJzBwYXJldCcgPSAnU3RhcnRpbmcgRGF0YXNldCcsICcxYWRhcHQnID0gJysgQWRhcHRpdmUgU2FtcGxpbmcnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJzJyYW5kbycgPSAnKyBSYW5kb20gU2FtcGxpbmcnKSwgbmFtZSA9ICcnLAogICAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gYygnMHBhcmV0JyA9ICdza3libHVlMicsICcxYWRhcHQnID0gJ3JlZCcsICcycmFuZG8nID0gJ2dyZWVuJykpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSBjKDAsIDAuMDUpKSkgKyBzY2FsZV94X2Rpc2NyZXRlKGJyZWFrcyA9ICcnKSArCiAgdGhlbWVfYncoKSArIGxhYnMoeCA9ICcnLCB5ID0gJ1RvdGFsXG5FcnJvciBSYXRlJywgc3VidGl0bGUgPSAnVXRvcGlhIERpc3RhbmNlICsgUHJpb3JpdHknKQoKZy50eXAxID0gZ2dwbG90KGVyci5zdm0ucmFkYW4pICsKICBnZW9tX2NvbChtYXBwaW5nID0gYWVzKHggPSBzb3VyY2UsIHkgPSB0eXAxLCBmaWxsID0gc291cmNlKSkgKwogIGdlb21fZXJyb3JiYXIobWFwcGluZyA9IGFlcyh4ID0gc291cmNlLCB5bWluID0gdHlwMSAtIHR5cDEuZXJyLCB5bWF4ID0gdHlwMSArIHR5cDEuZXJyKSwgd2lkdGggPSAwLjUpICsKICBmYWNldF93cmFwKH5tZXRob2QsIG5yb3cgPSAxKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobGFiZWxzID0gYygnMHBhcmV0JyA9ICdTdGFydGluZyBEYXRhc2V0JywgJzFhZGFwdCcgPSAnKyBBZGFwdGl2ZSBTYW1wbGluZycsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnMnJhbmRvJyA9ICcrIFJhbmRvbSBTYW1wbGluZycpLCBuYW1lID0gJycsCiAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBjKCcwcGFyZXQnID0gJ3NreWJsdWUyJywgJzFhZGFwdCcgPSAncmVkJywgJzJyYW5kbycgPSAnZ3JlZW4nKSkgKwogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBleHBhbnNpb24obXVsdCA9IGMoMCwgMC4wNSkpKSArIHNjYWxlX3hfZGlzY3JldGUoYnJlYWtzID0gJycpICsKICB0aGVtZV9idygpICsgbGFicyh4ID0gJycsIHkgPSAnRmFsc2UgUG9zaXRpdmVcbkVycm9yIFJhdGUnKQoKZy50eXAyID0gZ2dwbG90KGVyci5zdm0ucmFkYW4pICsKICBnZW9tX2NvbChtYXBwaW5nID0gYWVzKHggPSBzb3VyY2UsIHkgPSB0eXAyLCBmaWxsID0gc291cmNlKSkgKwogIGdlb21fZXJyb3JiYXIobWFwcGluZyA9IGFlcyh4ID0gc291cmNlLCB5bWluID0gdHlwMiAtIHR5cDIuZXJyLCB5bWF4ID0gdHlwMiArIHR5cDIuZXJyKSwgd2lkdGggPSAwLjUpICsKICBmYWNldF93cmFwKH5tZXRob2QsIG5yb3cgPSAxKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobGFiZWxzID0gYygnMHBhcmV0JyA9ICdTdGFydGluZyBEYXRhc2V0JywgJzFhZGFwdCcgPSAnKyBBZGFwdGl2ZSBTYW1wbGluZycsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnMnJhbmRvJyA9ICcrIFJhbmRvbSBTYW1wbGluZycpLCBuYW1lID0gJycsCiAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBjKCcwcGFyZXQnID0gJ3NreWJsdWUyJywgJzFhZGFwdCcgPSAncmVkJywgJzJyYW5kbycgPSAnZ3JlZW4nKSkgKwogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBleHBhbnNpb24obXVsdCA9IGMoMCwgMC4wNSkpKSArIHNjYWxlX3hfZGlzY3JldGUoYnJlYWtzID0gJycpICsKICB0aGVtZV9idygpICsgbGFicyh4ID0gJycsIHkgPSAnRmFsc2UgTmVnYXRpdmVcbkVycm9yIFJhdGUnKQoKKGcucmF0ZSArIGd1aWRlcyhmaWxsID0gRkFMU0UpKSAvIGcudHlwMSAvIChnLnR5cDIgKyBndWlkZXMoZmlsbCA9IEZBTFNFKSkKd3JpdGUuY3N2KGVyci5zdm0uY3V0b2YsICdTVk1fZXJyb3JfcmFkYW4uY3N2JykKCnJtKGVyci5wYXJldCwgZXJyLmFkYXB0LCBlcnIucmFuZG8sIGcucmF0ZSwgZy50eXAxLCBnLnR5cDIpCgpgYGAKCmBgYHtyIFNWTSBNYXJnaW5hbHM6IFV0b3BpYSBEaXN0YW5jZX0Kc3ZtLm1hcmdpbi5wYXJldCA9IG1hcmdpbmFsKG1vZCA9IG1vZC5wYXJldCk7IHN2bS5tYXJnaW4ucGFyZXQkY2F0ID0gJ3N0YXJ0Jwpzdm0ubWFyZ2luLmFkYXB0ID0gbWFyZ2luYWwobW9kID0gbW9kLmFkYXB0KTsgc3ZtLm1hcmdpbi5hZGFwdCRjYXQgPSAnYWRhcHQnCnN2bS5tYXJnaW4ucmFuZG8gPSBtYXJnaW5hbChtb2QgPSBtb2QucmFuZG8pOyBzdm0ubWFyZ2luLnJhbmRvJGNhdCA9ICdyYW5kbycKc3ZtLm1hcmdpbiA9IHJiaW5kKHN2bS5tYXJnaW4ucGFyZXQsIHN2bS5tYXJnaW4uYWRhcHQsIHN2bS5tYXJnaW4ucmFuZG8pCnJtKHN2bS5tYXJnaW4ucGFyZXQsIHN2bS5tYXJnaW4uYWRhcHQsIHN2bS5tYXJnaW4ucmFuZG8pCgp3cml0ZS5jc3Yoc3ZtLm1hcmdpbiwgJ01hcmdpbl9TVk1fcmFkYW4uY3N2JykKCkluZmVyLnBsdCA9IHJlYWQuY3N2KCcuLi9FeF9RdWFydGljL01hcmdpbmFsc19yYWRhbi5jc3YnKQpJbmZlci5wbHQgPSBJbmZlci5wbHRbLCFuYW1lcyhJbmZlci5wbHQpICVpbiUgYygnWCcpXQojIG5hbWVzKEluZmVyLnBsdCkKSW5mZXIucGx0JG1ldGhvZCA9ICdHUCcKCmdncGxvdCgpICsKICBnZW9tX3BhdGgoZGF0YSA9IGZpbHRlcihJbmZlci5wbHQsIGNhdCA9PSAndHJ1JyksIG1hcHBpbmcgPSBhZXMoeCA9IHgsIHkgPSBwcm9iIC0gcHNkLCBjb2xvciA9IGNhdCksIGxpbmV0eXBlID0gMykgKwogIGdlb21fcGF0aChkYXRhID0gZmlsdGVyKEluZmVyLnBsdCwgY2F0ID09ICd0cnUnKSwgbWFwcGluZyA9IGFlcyh4ID0geCwgeSA9IHByb2IgKyBwc2QsIGNvbG9yID0gY2F0KSwgbGluZXR5cGUgPSAzKSArCiAgZ2VvbV9wYXRoKGRhdGEgPSBzdm0ubWFyZ2luLCBtYXBwaW5nID0gYWVzKHggPSB4LCB5ID0gcHJvYiwgbGluZXR5cGUgPSBtZXRob2QsIGNvbG9yID0gY2F0KSkgKwogIGZhY2V0X3dyYXAofnZhciwgbnJvdyA9IDIpICsgdGhlbWVfYncoKSArCiAgbGFicyh4ID0gJ0lucHV0IFZhbHVlJywgeSA9ICdDb25kaXRpb25hbCBQcm9iYWJpbGl0eScsIGxpbmV0eXBlID0gJ1NWTSBLZXJuZWwnLCBjb2xvciA9ICcnLCAKICAgICAgIHN1YnRpdGxlID0gJ1V0b3BpYSBEaXN0YW5jZSArIFByaW9yaXR5JykgKwogIHNjYWxlX2NvbG9yX21hbnVhbChsYWJlbHMgPSBjKCd0cnUnID0gJ0V4cGVjdGVkIE1hcmdpbmFsJywgJ3N0YXJ0JyA9ICdTdGFydGluZyBEYXRhc2V0JywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnYWRhcHQnID0gJysgQWRhcHRpdmUgU2FtcGxpbmcnLCAncmFuZG8nID0gJysgUmFuZG9tIFNhbXBsaW5nJyksCiAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGMoJ3RydScgPSAnYmxhY2snLCAnc3RhcnQnID0gJ3NreWJsdWUyJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnYWRhcHQnID0gJ3JlZCcsICdyYW5kbycgPSAnZ3JlZW4nKSwKICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYygndHJ1JywgJ3N0YXJ0JywgJ2FkYXB0JywgJ3JhbmRvJykpICsKICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChsaW5ldHlwZSA9IGMoMywgMSwgMSwgMSkpKSkgKwogIHNjYWxlX2xpbmV0eXBlX2Rpc2NyZXRlKGxhYmVscyA9IGMoJ1NWTS1wb2wnID0gJ1BvbHlub21pYWwnLCAncmFkJyA9ICdTVk0tUmFkaWFsJykpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLCAwKSkgKyBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCAxKSkKCmdncGxvdCgpICsKICBnZW9tX3BhdGgoZGF0YSA9IGZpbHRlcihJbmZlci5wbHQsIGNhdCA9PSAndHJ1JyksIG1hcHBpbmcgPSBhZXMoeCA9IHgsIHkgPSBwcm9iIC0gcHNkLCBjb2xvciA9IGNhdCksIGxpbmV0eXBlID0gMykgKwogIGdlb21fcGF0aChkYXRhID0gZmlsdGVyKEluZmVyLnBsdCwgY2F0ID09ICd0cnUnKSwgbWFwcGluZyA9IGFlcyh4ID0geCwgeSA9IHByb2IgKyBwc2QsIGNvbG9yID0gY2F0KSwgbGluZXR5cGUgPSAzKSArCiAgZ2VvbV9wYXRoKGRhdGEgPSBmaWx0ZXIoSW5mZXIucGx0LCBjYXQgIT0gJ3RydScpLCBtYXBwaW5nID0gYWVzKHggPSB4LCB5ID0gcHJvYiwgY29sb3IgPSBjYXQpKSArCiAgZmFjZXRfd3JhcCh2YXJ+LiwgbnJvdyA9IDIpICsgCiAgc2NhbGVfY29sb3JfbWFudWFsKGxhYmVscyA9IGMoJ3RydScgPSAnRXhwZWN0ZWQgTWFyZ2luYWwnLCAnc3RhcnQnID0gJ1N0YXJ0aW5nIERhdGFzZXQnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdhZGFwdCcgPSAnKyBBZGFwdGl2ZSBTYW1wbGluZycsICdyYW5kbycgPSAnKyBSYW5kb20gU2FtcGxpbmcnKSwKICAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gYygndHJ1JyA9ICdibGFjaycsICdzdGFydCcgPSAnc2t5Ymx1ZTInLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdhZGFwdCcgPSAncmVkJywgJ3JhbmRvJyA9ICdncmVlbicpLAogICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKCd0cnUnLCAnc3RhcnQnLCAnYWRhcHQnLCAncmFuZG8nKSkgKwogIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KGxpbmV0eXBlID0gYygzLCAxLCAxLCAxKSkpKSArCiAgbGFicyh4ID0gJycsIHkgPSAnUHJvYmFiaWxpdHkgb2YgQWNjZXB0YW5jZScsIHN1YnRpdGxlID0gJ0N1dG9mZiBUaHJlc2hvbGQnLCBjb2xvciA9ICcnKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMC4wNSkpICsgc2NhbGVfeF9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCkpICsKICB0aGVtZV9idygpCgojIENhbGN1bGF0ZSBjb2VmZmljaWVudHMgb2YgZGV0ZXJtaW5hdGlvbjogZWFzaWVyIGNvbXBhcmlzb24KY29lZmRldCA9IGRhdGEuZnJhbWUobWV0aG9kID0gYyhyZXAoJ1NWTS1wb2wnLCAzKSwgcmVwKCdTVk0tcmFkJywgMyksIHJlcCgnR1AnLCAzKSksCiAgICAgICAgICAgZGF0YXNldCA9IGMoJzBzdGFydCcsICcxYWRhcHQnLCAnMnJhbmRvJyksCiBjb2VmID0gYyhjb3IoeCA9IGZpbHRlcihzdm0ubWFyZ2luLCBtZXRob2QgPT0gJ1NWTS1wb2wnLCBjYXQgPT0gJ3N0YXJ0JykkcHJvYiwgCiAgICB5ID0gZmlsdGVyKEluZmVyLnBsdCwgY2F0ID09ICd0cnUnKSRwcm9iLCBtZXRob2QgPSAncGVhcnNvbicpXjIsCiAgY29yKHggPSBmaWx0ZXIoc3ZtLm1hcmdpbiwgbWV0aG9kID09ICdTVk0tcG9sJywgY2F0ID09ICdhZGFwdCcpJHByb2IsIAogICAgeSA9IGZpbHRlcihJbmZlci5wbHQsIGNhdCA9PSAndHJ1JykkcHJvYiwgbWV0aG9kID0gJ3BlYXJzb24nKV4yLAogIGNvcih4ID0gZmlsdGVyKHN2bS5tYXJnaW4sIG1ldGhvZCA9PSAnU1ZNLXBvbCcsIGNhdCA9PSAncmFuZG8nKSRwcm9iLCAKICAgIHkgPSBmaWx0ZXIoSW5mZXIucGx0LCBjYXQgPT0gJ3RydScpJHByb2IsIG1ldGhvZCA9ICdwZWFyc29uJyleMiwKICBjb3IoeCA9IGZpbHRlcihzdm0ubWFyZ2luLCBtZXRob2QgPT0gJ1NWTS1yYWQnLCBjYXQgPT0gJ3N0YXJ0JykkcHJvYiwgCiAgICB5ID0gZmlsdGVyKEluZmVyLnBsdCwgY2F0ID09ICd0cnUnKSRwcm9iLCBtZXRob2QgPSAncGVhcnNvbicpXjIsCiAgY29yKHggPSBmaWx0ZXIoc3ZtLm1hcmdpbiwgbWV0aG9kID09ICdTVk0tcmFkJywgY2F0ID09ICdhZGFwdCcpJHByb2IsIAogICAgeSA9IGZpbHRlcihJbmZlci5wbHQsIGNhdCA9PSAndHJ1JykkcHJvYiwgbWV0aG9kID0gJ3BlYXJzb24nKV4yLAogIGNvcih4ID0gZmlsdGVyKHN2bS5tYXJnaW4sIG1ldGhvZCA9PSAnU1ZNLXJhZCcsIGNhdCA9PSAncmFuZG8nKSRwcm9iLCAKICAgIHkgPSBmaWx0ZXIoSW5mZXIucGx0LCBjYXQgPT0gJ3RydScpJHByb2IsIG1ldGhvZCA9ICdwZWFyc29uJyleMiwKICBjb3IoeCA9IGZpbHRlcihJbmZlci5wbHQsIGNhdCA9PSAnc3RhcnQnKSRwcm9iLCAKICAgIHkgPSBjKGZpbHRlcihJbmZlci5wbHQsIGNhdCA9PSAndHJ1JywgdmFyID09ICd4MScpJHByb2IsIGZpbHRlcihJbmZlci5wbHQsIGNhdCA9PSAndHJ1JywgdmFyID09ICd4MicpJHByb2IpLCAKICAgIG1ldGhvZCA9ICdwZWFyc29uJyleMiwKICBjb3IoeCA9IGZpbHRlcihJbmZlci5wbHQsIGNhdCA9PSAnYWRhcHQnKSRwcm9iLCAKICAgIHkgPSBjKGZpbHRlcihJbmZlci5wbHQsIGNhdCA9PSAndHJ1JywgdmFyID09ICd4MScpJHByb2IsIGZpbHRlcihJbmZlci5wbHQsIGNhdCA9PSAndHJ1JywgdmFyID09ICd4MicpJHByb2IpLCAKICAgIG1ldGhvZCA9ICdwZWFyc29uJyleMiwKICBjb3IoeCA9IGZpbHRlcihJbmZlci5wbHQsIGNhdCA9PSAncmFuZG8nKSRwcm9iLCAKICAgIHkgPSBjKGZpbHRlcihJbmZlci5wbHQsIGNhdCA9PSAndHJ1JywgdmFyID09ICd4MScpJHByb2IsIGZpbHRlcihJbmZlci5wbHQsIGNhdCA9PSAndHJ1JywgdmFyID09ICd4MicpJHByb2IpLCAKICAgIG1ldGhvZCA9ICdwZWFyc29uJyleMikpCgpjb2VmZGV0CndyaXRlLmNzdihjb2VmZGV0LCAnTWFyZ2luYWxzX0NvZWZEZXRfcmFkYW4uY3N2JykKCmdncGxvdChjb2VmZGV0KSArCiAgZ2VvbV9jb2wobWFwcGluZyA9IGFlcyh4ID0gZGF0YXNldCwgZmlsbCA9IGRhdGFzZXQsIHkgPSBjb2VmKSkrCiAgZmFjZXRfZ3JpZCh+bWV0aG9kKSArCiAgbGFicyh4ID0gJycsIHkgPSAnMS1WYXJpYWJsZSBNYXJnaW5hbCBDb2VmZmljaWVudCBvZiBEZXRlcm1pbmF0aW9uJywgc3VidGl0bGUgPSAnVXRvcGlhIERpc3RhbmNlICsgUHJpb3JpdHknKSArCiAgc2NhbGVfeF9kaXNjcmV0ZShicmVha3MgPSBjKCkpICsKICBzY2FsZV9maWxsX21hbnVhbChsYWJlbHMgPSBjKCcwc3RhcnQnID0gJ1N0YXJ0aW5nIERhdGFzZXQnLCAnMWFkYXB0JyA9ICcrIEFkYXB0aXZlIFNhbXBsaW5nJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICcycmFuZG8nID0gJysgUmFuZG9tIFNhbXBsaW5nJyksIG5hbWUgPSAnJywKICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBjKCcwc3RhcnQnID0gJ3NreWJsdWUyJywgJzFhZGFwdCcgPSAncmVkJywgJzJyYW5kbycgPSAnZ3JlZW4nKSkgKwogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBleHBhbnNpb24obXVsdCA9IGMoMCwgMC4wNSkpKQoKYGBgCgpgYGB7ciBJbXBvcnRhbmNlIFJhbmtpbmcgTWV0aG9kIENvbXBhcmlzb246IENhbGN1bGF0aW5nIE1hcmdpbmFsIEltcG9ydGFuY2UgZm9yIFNWTX0KaW1wb3J0Lm1hcmdpbmFsID0gZnVuY3Rpb24oc3ZtLm1hcmdpbiwgdHlwKXsKICAjIFNldCB1cCBvdXRwdXQKICBpbXBvcnQuc3ZtLm1hcmdpbiA9IGRhdGEuZnJhbWUoKQogICMgTG9vcCBhY3Jvc3MgbWV0aG9kcwogIGZvcihtZXQgaW4gdW5pcXVlKHN2bS5tYXJnaW4kbWV0aG9kKSl7CiAgICBzdWIgPSBmaWx0ZXIoc3ZtLm1hcmdpbiwgY2F0ID09ICdhZGFwdCcsIHZhciA9PSAneDEnLCBtZXRob2QgPT0gbWV0KQogICAgaW1wb3J0LnN2bS5tYXJnaW4gPSByYmluZChpbXBvcnQuc3ZtLm1hcmdpbiwgZGF0YS5mcmFtZSgKICAgICAgaW1wb3J0ID0gZGlmZihyYW5nZShzdWIkcHJvYikpL3N1bShzdWIkcHNkKSwKICAgICAgdmFyID0gJ3gxJywKICAgICAgbWV0aG9kID0gcGFzdGUoJ01hcmdpbmFsJywgc3Vic3RyaW5nKG1ldCwgNCksIHNlcCA9ICcnKSwKICAgICAgc2QgPSBzcXJ0KChtYXgoc3ViJHByb2IpKigxLW1heChzdWIkcHJvYikpICsgbWluKHN1YiRwcm9iKSooMS1taW4oc3ViJHByb2IpKSAqIAogICAgICAgICAgICAgICAgICAgIGRpZmYocmFuZ2Uoc3ViJHByb2IpKSArIHZhcihzdWIkcHNkKSkvbnJvdyhzdWIpKQogICAgICApKQogICAgc3ViID0gZmlsdGVyKHN2bS5tYXJnaW4sIGNhdCA9PSAnYWRhcHQnLCB2YXIgPT0gJ3gyJywgbWV0aG9kID09IG1ldCkKICAgIGltcG9ydC5zdm0ubWFyZ2luID0gcmJpbmQoaW1wb3J0LnN2bS5tYXJnaW4sIGRhdGEuZnJhbWUoCiAgICAgIGltcG9ydCA9IGRpZmYocmFuZ2Uoc3ViJHByb2IpKS9zdW0oc3ViJHBzZCksCiAgICAgIHZhciA9ICd4MicsCiAgICAgIG1ldGhvZCA9IHBhc3RlKCdNYXJnaW5hbCcsIHN1YnN0cmluZyhtZXQsIDQpLCBzZXAgPSAnJyksCiAgICAgIHNkID0gc3FydCgobWF4KHN1YiRwcm9iKSooMS1tYXgoc3ViJHByb2IpKSArIG1pbihzdWIkcHJvYikqKDEtbWluKHN1YiRwcm9iKSkgKiAKICAgICAgICAgICAgICAgICAgICBkaWZmKHJhbmdlKHN1YiRwcm9iKSkgKyB2YXIoc3ViJHBzZCkpL25yb3coc3ViKSkKICAgICAgKSkKICB9CiAgIyBUeXBlLCByZWxhdGl2ZSBpbXBvcnRhbmNlCiAgaW1wb3J0LnN2bS5tYXJnaW4kdHlwID0gdHlwCiAgaW1wb3J0LnN2bS5tYXJnaW4kci5pbXBvcnQgPSBjKGltcG9ydC5zdm0ubWFyZ2luJGltcG9ydFsxOjJdL21heChpbXBvcnQuc3ZtLm1hcmdpbiRpbXBvcnRbMToyXSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGltcG9ydC5zdm0ubWFyZ2luJGltcG9ydFszOjRdL21heChpbXBvcnQuc3ZtLm1hcmdpbiRpbXBvcnRbMzo0XSkpCiAgcmV0dXJuKGltcG9ydC5zdm0ubWFyZ2luKQp9Cgpzdm0uaW1wb3J0Lm1hcmdpbiA9IHJiaW5kKGltcG9ydC5tYXJnaW5hbChzdm0ubWFyZ2luID0gcmVhZC5jc3YoJ01hcmdpbl9TVk1fZGlzdC5jc3YnKSwgdHlwID0gJ1BhcmV0byBEaXN0YW5jZScpLCAKICAgICAgaW1wb3J0Lm1hcmdpbmFsKHN2bS5tYXJnaW4gPSByZWFkLmNzdignTWFyZ2luX1NWTV9jdXRvZi5jc3YnKSwgdHlwID0gJ1RocmVzaG9sZCBDdXRvZmYnKSwgCiAgICAgIGltcG9ydC5tYXJnaW5hbChzdm0ubWFyZ2luID0gcmVhZC5jc3YoJ01hcmdpbl9TVk1fcmFkYW4uY3N2JyksIHR5cCA9ICdVdG9waWEgRGlzdGFuY2UnKSkKYGBgCgoKYGBge3IgU1ZNOiBJbXBvcnRhbmNlIFJhbmtpbmdzfQppbXBvcnQucmFuayA9IHJlYWQuY3N2KCcuLi9FeF9RdWFydGljL0ltcG9ydGFuY2UuY3N2JykKaW1wb3J0LnJhbmsgPSBpbXBvcnQucmFua1ssICFuYW1lcyhpbXBvcnQucmFuaykgJWluJSBjKCdYJyldCmltcG9ydC5yYW5rJG1ldGhvZCA9IHVubGlzdChsYXBwbHkoaW1wb3J0LnJhbmskbWV0aG9kLCBmdW5jdGlvbih4KSBwYXN0ZSh4LCAnLUdQJywgc2VwID0gJycpKSkKaW1wb3J0LnJhbmsKIyBpbXBvcnQucmFuayRtZXRob2RbaW1wb3J0LnJhbmskbWV0aG9kID09ICdTaGFwbGV5J10gPSAnU2hhcGxleS1HUCcKClNWTS5zaGFwLmRlbHRhID0gU1ZNLnNoYXAobW9kID0gbW9kLmFkYXB0LmRlbHRhKQpTVk0uc2hhcC5kZWx0YSR0eXAgPSAnUGFyZXRvIERpc3RhbmNlJwpTVk0uc2hhcC5jdXRvZiA9IFNWTS5zaGFwKG1vZCA9IG1vZC5hZGFwdC5jdXRvZikKU1ZNLnNoYXAuY3V0b2YkdHlwID0gJ1RocmVzaG9sZCBDdXRvZmYnClNWTS5zaGFwLnJhZGFuID0gU1ZNLnNoYXAobW9kID0gbW9kLmFkYXB0LnJhZGFuKQpTVk0uc2hhcC5yYWRhbiR0eXAgPSAnVXRvcGlhIERpc3RhbmNlJwoKCmdncGxvdChyYmluZChpbXBvcnQucmFuaywgU1ZNLnNoYXAuZGVsdGEsIFNWTS5zaGFwLmN1dG9mLCBTVk0uc2hhcC5yYWRhbiwgc3ZtLmltcG9ydC5tYXJnaW4pKSArCiAgZ2VvbV9jb2wobWFwcGluZyA9IGFlcyh4ID0gdmFyLCB5ID0gYWJzKHIuaW1wb3J0KSwgZmlsbCA9IHZhcikpICsKICBmYWNldF9ncmlkKG1ldGhvZH50eXAsIHNjYWxlcyA9ICdmcmVlX3knKSArCiAgbGFicyh4ID0gJycsIHkgPSAnUmVsYXRpdmUgSW1wb3J0YW5jZScpICsKICBndWlkZXMoZmlsbCA9IEZBTFNFKSArIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzID0gYygneDEnID0gZXhwcmVzc2lvbigneCdbMV0pLCAneDInID0gZXhwcmVzc2lvbigneCdbMl0pKSkKCmdncGxvdChyYmluZChpbXBvcnQucmFuaywgU1ZNLnNoYXAuZGVsdGEsIFNWTS5zaGFwLmN1dG9mLCBTVk0uc2hhcC5yYWRhbiwgc3ZtLmltcG9ydC5tYXJnaW4pKSArCiAgZ2VvbV9jb2wobWFwcGluZyA9IGFlcyh4ID0gdmFyLCB5ID0gaW1wb3J0LCBmaWxsID0gdmFyKSkgKwogIGZhY2V0X2dyaWQobWV0aG9kfnR5cCwgc2NhbGVzID0gJ2ZyZWVfeScpICsKICBsYWJzKHggPSAnJywgeSA9ICdSZWxhdGl2ZSBJbXBvcnRhbmNlJykgKwogIGd1aWRlcyhmaWxsID0gRkFMU0UpICsgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHMgPSBjKCd4MScgPSBleHByZXNzaW9uKCd4J1sxXSksICd4MicgPSBleHByZXNzaW9uKCd4J1syXSkpKQoKIyBSb3dzOiBTZWxlY3Rpb24gbWV0aG9kID0gUGFyZXRvIGRpc3RhbmNlLCBPYmplY3RpdmUgQ3V0b2ZmLCBVdG9waWEgRGlzdGFuY2UgKyBQcmlvcml0eQojIENvbHVtbnM6IG1ldGhvZCA9IFNoYXBsZXkgd2l0aCBTVk0sIFNoYXBsZXkgd2l0aCBHUCwgbmV3IG1ldGhvZAoKYGBgCgpgYGB7cn0KIyBTcGxpdCB1cCBieSBtb2RlbCBtZXRob2QKaW1wb3J0LmFsbCA9IHJiaW5kKGltcG9ydC5yYW5rLCBTVk0uc2hhcC5kZWx0YSwgU1ZNLnNoYXAuY3V0b2YsIFNWTS5zaGFwLnJhZGFuLCBzdm0uaW1wb3J0Lm1hcmdpbikKIyBDbGFzc2lmaWNhdGlvbiBzcGxpdApzcGxpdCA9IHVubGlzdChsYXBwbHkoaW1wb3J0LmFsbCRtZXRob2QsIHN0cnNwbGl0LCAnLScpKQppbXBvcnQuYWxsJG1ldGhvZCA9IHNwbGl0W2MoVFJVRSwgRkFMU0UpXQppbXBvcnQuYWxsJG1vZGVsID0gc3BsaXRbYyhGQUxTRSwgVFJVRSldCgppbXBvcnQuYWxsCmdncGxvdChpbXBvcnQuYWxsKSArCiAgZ2VvbV9jb2wobWFwcGluZyA9IGFlcyh4ID0gbW9kZWwsIHkgPSBpbXBvcnQsIGZpbGwgPSB2YXIpLCBwb3NpdGlvbiA9ICdkb2RnZScpICsKICBnZW9tX2Vycm9yYmFyKG1hcHBpbmcgPSBhZXMoeCA9IG1vZGVsLCB5bWF4ID0gaW1wb3J0ICsgc2QsIHltaW4gPSBpbXBvcnQgLSBzZCwgZ3JvdXAgPSB2YXIpLCAKICAgICAgICAgICAgICAgIHBvc2l0aW9uID0gJ2RvZGdlJywgd2lkdGggPSAwLjkpICsKICBmYWNldF9ncmlkKG1ldGhvZH50eXAsIHNjYWxlcyA9ICdmcmVlX3knKSArCiAgbGFicyh4ID0gJ01vZGVsJywgeSA9ICdSZWxhdGl2ZSBJbXBvcnRhbmNlIChVbml0bGVzcyknKSArCiAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHMgPSBjKCdHUCcsICdwb2wnID0gJ1NWTS1wb2wnLCAncmFkJyA9ICdTVk0tcmFkJykpICsKICBzY2FsZV9maWxsX2Rpc2NyZXRlKG5hbWUgPSAnJywgbGFiZWxzID0gYygneDEnID0gZXhwcmVzc2lvbigneCdbMV0pLCAneDInID0gZXhwcmVzc2lvbigneCdbMl0pKSkKCiMgUmVsYXRpdmUgaW1wb3J0YW5jZSAobm9ybWFsaXplZCk6IHVuY2VydGFpbnR5IGlzIGRpdmlkZWQgYnkgdGhlIHNhbWUgdmFsdWUKci5tYXggPSBpbXBvcnQuYWxsJGltcG9ydApmb3IoaSBpbiBzZXEoZnJvbSA9IDEsIHRvID0gbGVuZ3RoKHIubWF4KS0xLCBieSA9IDIpKXsKICByLm1heFtjKGksaSsxKV0gPSBtYXgoYWJzKHIubWF4W2MoaSxpKzEpXSkpCn0KaW1wb3J0LmFsbCRyLmltcG9ydCA9IGFicyhpbXBvcnQuYWxsJGltcG9ydCkvci5tYXgKaW1wb3J0LmFsbCRyLnNkID0gaW1wb3J0LmFsbCRzZCAvIHIubWF4CmdncGxvdChpbXBvcnQuYWxsKSArCiAgZ2VvbV9jb2wobWFwcGluZyA9IGFlcyh4ID0gbW9kZWwsIHkgPSByLmltcG9ydCwgZmlsbCA9IHZhciksIHBvc2l0aW9uID0gJ2RvZGdlJykgKwogIGdlb21fZXJyb3JiYXIobWFwcGluZyA9IGFlcyh4ID0gbW9kZWwsIHltYXggPSByLmltcG9ydCArIHIuc2QsIHltaW4gPSByLmltcG9ydCAtIHIuc2QsIGdyb3VwID0gdmFyKSwgCiAgICAgICAgICAgICAgICBwb3NpdGlvbiA9ICdkb2RnZScsIHdpZHRoID0gMC45KSArCiAgZmFjZXRfZ3JpZChtZXRob2R+dHlwLCBzY2FsZXMgPSAnZnJlZV95JykgKwogIGxhYnMoeCA9ICdNb2RlbCcsIHkgPSAnUmVsYXRpdmUgSW1wb3J0YW5jZSAoVW5pdGxlc3MpJykgKwogIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzID0gYygnR1AnLCAncG9sJyA9ICdTVk0tcG9sJywgJ3JhZCcgPSAnU1ZNLXJhZCcpKSArCiAgc2NhbGVfZmlsbF9kaXNjcmV0ZShuYW1lID0gJycsIGxhYmVscyA9IGMoJ3gxJyA9IGV4cHJlc3Npb24oJ3gnWzFdKSwgJ3gyJyA9IGV4cHJlc3Npb24oJ3gnWzJdKSkpCgpybShyLm1heCkKCndyaXRlLmNzdihpbXBvcnQuYWxsLCAnSW1wb3J0UmFua19NZXRob2QuY3N2JykKYGBgCgojIEV4aXN0aW5nIE1ldGhvZDogR2F1c3NpYW4gTWl4dHVyZXMKClNpbmNlIHRoZSBHTSBtb2RlbHMgYXJlIHVuc3VwZXJ2aXNlZCwgdGhlcmUgY2Fubm90IGJlIGNvbnRyb2wgZm9yIHdoYXQgdGhlIHNlbGVjdGlvbiBjcml0ZXJpYSBhcmUuIEEgc3VwZXJmaWNpYWwgYW5hbHlzaXMgd2lsbCBiZSBjb25kdWN0ZWQgdG8gY2hlY2sgd2hhdCB0aGUgTUwgYWxnb3JpdGhtIGlzIGNvbnZlcmdpbmcgdG8sIGJ1dCB0aGUgcmVzdWx0cyB3aWxsIGJlIGludGVycHJldGVkIHNvbGVseSBpbiB0ZXJtcyBvZiBkZXNjcmlwdGl2ZSBzdGF0aXN0aWNzLgoKRm9yIHRoZSBwdXJwb3NlcyBvZiBhbmFseXNpcywgdGhyZWUgbW9kZWxzIHdpbGwgYmUgdGVzdGVkIGJhc2VkIG9uIHRoZSBpbnB1dCB0byB0aGUgTUwgYWxnb3JpdGhtOgoqICh4MSwgeDIsIGYxLCBmMik6IHRoZSBmdWxsIHNldCBvZiBpbnB1dHMgYW5kIG91dHB1dHMgLSBzaW5jZSBpdCBoYXMgdGhlIG1vc3QgZGF0YSwgdGhpcyB3aWxsIGxpa2VseSBiZSB0aGUgbW9zdCByb2J1c3QKKiAoeDEsIHgyKTogbmVnYXRpdmUgY29udHJvbCBvZiBqdXN0IHRoZSBpbnB1dHMgLSBpdCBzaG91bGQgZ3JvdXAgdGhlIHBvaW50cyBiYXNlZCBvbiB0aGUgc2FtcGxpbmcgZGVuc2l0aWVzCiogKGYxLCBmMik6IG91dHB1dHMgb25seSAtIHRoaXMgc2hvdWxkIGhhdmUgdGhlIGJlc3QgZGlzdGluY3Rpb24gYmV0d2VlbiBnb29kIGFuZCBiYWQgcGVyZm9ybWluZyBncm91cHMKClVwIHRvIDEyIGNhdGVnb3JpZXMgd2lsbCBiZSBhbGxvd2VkLCBhbmQgYW55IHR5cGUgb2YgR2F1c3NpYW4gbW9kZWxzIHdpbGwgYmUgYWNjZXB0ZWQuIFRoaXMgd2lsbCBsZWFkIHRvIGxvbmdlciBmaXR0aW5nIHRpbWUsIGJ1dCBzaG91bGQgaGVscCB3aXRoIGFjY3VyYWN5LgoKYGBge3J9CkdQYXIuYWxsID0gcmVhZC5jc3YoZmlsZSA9ICcuLi9FeF9RdWFydGljL0dQYXJfYWxsX3N0YXJ0LmNzdicpCgojIG5hbWVzKEdQYXIuYWxsKQptYXguY2F0ID0gMTIKY2x1c3Rlci5hbGwgPSBNY2x1c3QoZGF0YSA9IEdQYXIuYWxsWyxjKCd4MScsICd4MicsICdmMScsICdmMicpXSwgRyA9IDI6bWF4LmNhdCkKY2x1c3Rlci5pbiA9IE1jbHVzdChkYXRhID0gR1Bhci5hbGxbLGMoJ3gxJywgJ3gyJyldLCBHID0gMjptYXguY2F0KQpjbHVzdGVyLm91dCA9IE1jbHVzdChkYXRhID0gR1Bhci5hbGxbLGMoJ2YxJywgJ2YyJyldLCBHID0gMjptYXguY2F0KQoKYGBgCgpgYGB7cn0KIyBzdW1tYXJ5KGNsdXN0ZXIsIHBhcmFtZXRlcnMgPSBUUlVFKQoKR1Bhci5hbGwkdHlwLmFsbCA9IGNsdXN0ZXIuYWxsJGNsYXNzaWZpY2F0aW9uCkdQYXIuYWxsJHR5cC5pbiA9IGNsdXN0ZXIuaW4kY2xhc3NpZmljYXRpb24KR1Bhci5hbGwkdHlwLm91dCA9IGNsdXN0ZXIub3V0JGNsYXNzaWZpY2F0aW9uCgpnZ3Bsb3QoKSArCiAgIyBCb3VuZGFyaWVzOiArLy0gc29tZSBzZXBhcmF0aW9uIGZyb20gMC41CiAgZ2VvbV9jb250b3VyKGRhdGEgPSBmaW5lLmdyaWQsIG1hcHBpbmcgPSBhZXMoeCA9IHgxLCB5ID0geDIsIHogPSBkaXN0LCBjb2xvciA9ICdkZWx0YScpLCBicmVha3MgPSBjKDEpKSArCiAgZ2VvbV9jb250b3VyKGRhdGEgPSBmaW5lLmdyaWQsIG1hcHBpbmcgPSBhZXMoeCA9IHgxLCB5ID0geDIsIHogPSBjdXRvZiwgY29sb3IgPSAnY3V0b2YnKSwgYnJlYWtzID0gYygwLjUpKSArCiAgZ2VvbV9jb250b3VyKGRhdGEgPSBmaW5lLmdyaWQsIG1hcHBpbmcgPSBhZXMoeCA9IHgxLCB5ID0geDIsIHogPSByYWQsIGNvbG9yID0gJ3JhZCcpLCBicmVha3MgPSBjKDEpKSArCiAgZ2VvbV9jb250b3VyKGRhdGEgPSBmaW5lLmdyaWQsIG1hcHBpbmcgPSBhZXMoeCA9IHgxLCB5ID0geDIsIHogPSBhbmcsIGNvbG9yID0gJ2FuZycpLCBicmVha3MgPSBjKDUwKSkgKwogICMgUGFyZXRvIGZyb250aWVyCiAgZ2VvbV9zbW9vdGgoZGF0YSA9IEdQYXIuZnJvbnQsIG1hcHBpbmcgPSBhZXMoeCA9IHgxLCB5ID0geDIsIGNvbG9yID0gJ1BhcmV0bycpLCAKICAgICAgICAgICAgICBsZXZlbCA9IDAuOTUsIGZvcm11bGEgPSAoeX54KSwgbWV0aG9kID0gJ2xvZXNzJykgKyAKICAjIENsdXN0ZXJpbmcKICBnZW9tX3BvaW50KGRhdGEgPSBHUGFyLmFsbCwgbWFwcGluZyA9IGFlcyh4ID0geDEsIHkgPSB4MiwgZmlsbCA9IGFzLmZhY3Rvcih0eXAuYWxsKSksIHNpemUgPSAyLjUsIHNoYXBlID0gMjEpICsKCiAgbGFicyh4ID0gZXhwcmVzc2lvbigneCdbMV0pLCB5ID0gZXhwcmVzc2lvbigneCdbMl0pLCAKICAgICAgIGNvbG9yID0gJ0FjY2VwdGFuY2UgQ3JpdGVyaWEnLCBmaWxsID0gJ0dyb3VwJywgCiAgICAgICBzdWJ0aXRsZSA9ICcoeDEsIHgyLCBmMSwgZjIpJykgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCdkZWx0YScgPSAnIzFiOWU3NycsICdjdXRvZicgPSAnI2Q5NWYwMicsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdyYWQnID0gJyM3NTcwYjMnLCAnYW5nJyA9ICcjZTcyOThhJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1BhcmV0bycgPSAnYmxhY2snKSwKICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygnZGVsdGEnID0gZXhwcmVzc2lvbihkZWx0YSonIDwgMScpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnY3V0b2YnID0gZXhwcmVzc2lvbignRidbMV1eJyonKic8IDEsIEYnWzJdXicqJyonPCAxJyksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdyYWQnID0gZXhwcmVzc2lvbignciA8IDEnKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnYW5nJyA9IGV4cHJlc3Npb24oJ0YnWzFdKidhbmQgRidbMl0qJyBCYWxhbmNlJyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1BhcmV0bycgPSBleHByZXNzaW9uKCdQYXJldG8gRnJvbnQnKSksCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoJ2RlbHRhJywgJ2N1dG9mJywgJ3JhZCcsICdhbmcnLCAnUGFyZXRvJykpICsKICB0aGVtZV9jbGFzc2ljKCkgKyAjdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gYygwLjg1LCAwLjc1KSkgKyAKICBzY2FsZV94X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLCAwKSwgbGltaXRzID0gYygwLCA1KSkgKyBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLCAwKSwgbGltaXRzID0gYygwLCA1KSkgKwogIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChmaWxsID0gYWxwaGEoJ3doaXRlJywgMSkpKSkKCmdncGxvdCgpICsKICAjIEJvdW5kYXJpZXM6ICsvLSBzb21lIHNlcGFyYXRpb24gZnJvbSAwLjUKICBnZW9tX2NvbnRvdXIoZGF0YSA9IGZpbmUuZ3JpZCwgbWFwcGluZyA9IGFlcyh4ID0geDEsIHkgPSB4MiwgeiA9IGRpc3QsIGNvbG9yID0gJ2RlbHRhJyksIGJyZWFrcyA9IGMoMSkpICsKICBnZW9tX2NvbnRvdXIoZGF0YSA9IGZpbmUuZ3JpZCwgbWFwcGluZyA9IGFlcyh4ID0geDEsIHkgPSB4MiwgeiA9IGN1dG9mLCBjb2xvciA9ICdjdXRvZicpLCBicmVha3MgPSBjKDAuNSkpICsKICBnZW9tX2NvbnRvdXIoZGF0YSA9IGZpbmUuZ3JpZCwgbWFwcGluZyA9IGFlcyh4ID0geDEsIHkgPSB4MiwgeiA9IHJhZCwgY29sb3IgPSAncmFkJyksIGJyZWFrcyA9IGMoMSkpICsKICBnZW9tX2NvbnRvdXIoZGF0YSA9IGZpbmUuZ3JpZCwgbWFwcGluZyA9IGFlcyh4ID0geDEsIHkgPSB4MiwgeiA9IGFuZywgY29sb3IgPSAnYW5nJyksIGJyZWFrcyA9IGMoNTApKSArCiAgIyBQYXJldG8gZnJvbnRpZXIKICBnZW9tX3Ntb290aChkYXRhID0gR1Bhci5mcm9udCwgbWFwcGluZyA9IGFlcyh4ID0geDEsIHkgPSB4MiwgY29sb3IgPSAnUGFyZXRvJyksIAogICAgICAgICAgICAgIGxldmVsID0gMC45NSwgZm9ybXVsYSA9ICh5fngpLCBtZXRob2QgPSAnbG9lc3MnKSArIAogICMgQ2x1c3RlcmluZwogIGdlb21fcG9pbnQoZGF0YSA9IEdQYXIuYWxsLCBtYXBwaW5nID0gYWVzKHggPSB4MSwgeSA9IHgyLCBmaWxsID0gYXMuZmFjdG9yKHR5cC5pbikpLCBzaXplID0gMi41LCBzaGFwZSA9IDIxKSArCgogIGxhYnMoeCA9IGV4cHJlc3Npb24oJ3gnWzFdKSwgeSA9IGV4cHJlc3Npb24oJ3gnWzJdKSwgCiAgICAgICBjb2xvciA9ICdBY2NlcHRhbmNlIENyaXRlcmlhJywgZmlsbCA9ICdHcm91cCcsIAogICAgICAgc3VidGl0bGUgPSAnKHgxLCB4MiknKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoJ2RlbHRhJyA9ICcjMWI5ZTc3JywgJ2N1dG9mJyA9ICcjZDk1ZjAyJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ3JhZCcgPSAnIzc1NzBiMycsICdhbmcnID0gJyNlNzI5OGEnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnUGFyZXRvJyA9ICdibGFjaycpLAogICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCdkZWx0YScgPSBleHByZXNzaW9uKGRlbHRhKicgPCAxJyksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdjdXRvZicgPSBleHByZXNzaW9uKCdGJ1sxXV4nKicqJzwgMSwgRidbMl1eJyonKic8IDEnKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ3JhZCcgPSBleHByZXNzaW9uKCdyIDwgMScpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdhbmcnID0gZXhwcmVzc2lvbignRidbMV0qJ2FuZCBGJ1syXSonIEJhbGFuY2UnKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnUGFyZXRvJyA9IGV4cHJlc3Npb24oJ1BhcmV0byBGcm9udCcpKSwKICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYygnZGVsdGEnLCAnY3V0b2YnLCAncmFkJywgJ2FuZycsICdQYXJldG8nKSkgKwogIHRoZW1lX2NsYXNzaWMoKSArICN0aGVtZShsZWdlbmQucG9zaXRpb24gPSBjKDAuODUsIDAuNzUpKSArIAogIHNjYWxlX3hfY29udGludW91cyhleHBhbmQgPSBjKDAsIDApLCBsaW1pdHMgPSBjKDAsIDUpKSArIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBjKDAsIDApLCBsaW1pdHMgPSBjKDAsIDUpKSArCiAgZ3VpZGVzKGNvbG91ciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KGZpbGwgPSBhbHBoYSgnd2hpdGUnLCAxKSkpKQoKZ2dwbG90KCkgKwogICMgQm91bmRhcmllczogKy8tIHNvbWUgc2VwYXJhdGlvbiBmcm9tIDAuNQogIGdlb21fY29udG91cihkYXRhID0gZmluZS5ncmlkLCBtYXBwaW5nID0gYWVzKHggPSB4MSwgeSA9IHgyLCB6ID0gZGlzdCwgY29sb3IgPSAnZGVsdGEnKSwgYnJlYWtzID0gYygxKSkgKwogIGdlb21fY29udG91cihkYXRhID0gZmluZS5ncmlkLCBtYXBwaW5nID0gYWVzKHggPSB4MSwgeSA9IHgyLCB6ID0gY3V0b2YsIGNvbG9yID0gJ2N1dG9mJyksIGJyZWFrcyA9IGMoMC41KSkgKwogIGdlb21fY29udG91cihkYXRhID0gZmluZS5ncmlkLCBtYXBwaW5nID0gYWVzKHggPSB4MSwgeSA9IHgyLCB6ID0gcmFkLCBjb2xvciA9ICdyYWQnKSwgYnJlYWtzID0gYygxKSkgKwogIGdlb21fY29udG91cihkYXRhID0gZmluZS5ncmlkLCBtYXBwaW5nID0gYWVzKHggPSB4MSwgeSA9IHgyLCB6ID0gYW5nLCBjb2xvciA9ICdhbmcnKSwgYnJlYWtzID0gYyg1MCkpICsKICAjIFBhcmV0byBmcm9udGllcgogIGdlb21fc21vb3RoKGRhdGEgPSBHUGFyLmZyb250LCBtYXBwaW5nID0gYWVzKHggPSB4MSwgeSA9IHgyLCBjb2xvciA9ICdQYXJldG8nKSwgCiAgICAgICAgICAgICAgbGV2ZWwgPSAwLjk1LCBmb3JtdWxhID0gKHl+eCksIG1ldGhvZCA9ICdsb2VzcycpICsgCiAgIyBDbHVzdGVyaW5nCiAgZ2VvbV9wb2ludChkYXRhID0gR1Bhci5hbGwsIG1hcHBpbmcgPSBhZXMoeCA9IHgxLCB5ID0geDIsIGZpbGwgPSBhcy5mYWN0b3IodHlwLm91dCkpLCBzaXplID0gMi41LCBzaGFwZSA9IDIxKSArCgogIGxhYnMoeCA9IGV4cHJlc3Npb24oJ3gnWzFdKSwgeSA9IGV4cHJlc3Npb24oJ3gnWzJdKSwgCiAgICAgICBjb2xvciA9ICdBY2NlcHRhbmNlIENyaXRlcmlhJywgZmlsbCA9ICdHcm91cCcsIAogICAgICAgc3VidGl0bGUgPSAnKGYxLCBmMiknKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoJ2RlbHRhJyA9ICcjMWI5ZTc3JywgJ2N1dG9mJyA9ICcjZDk1ZjAyJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ3JhZCcgPSAnIzc1NzBiMycsICdhbmcnID0gJyNlNzI5OGEnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnUGFyZXRvJyA9ICdibGFjaycpLAogICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCdkZWx0YScgPSBleHByZXNzaW9uKGRlbHRhKicgPCAxJyksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdjdXRvZicgPSBleHByZXNzaW9uKCdGJ1sxXV4nKicqJzwgMSwgRidbMl1eJyonKic8IDEnKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ3JhZCcgPSBleHByZXNzaW9uKCdyIDwgMScpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdhbmcnID0gZXhwcmVzc2lvbignRidbMV0qJ2FuZCBGJ1syXSonIEJhbGFuY2UnKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnUGFyZXRvJyA9IGV4cHJlc3Npb24oJ1BhcmV0byBGcm9udCcpKSwKICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYygnZGVsdGEnLCAnY3V0b2YnLCAncmFkJywgJ2FuZycsICdQYXJldG8nKSkgKwogIHRoZW1lX2NsYXNzaWMoKSArICN0aGVtZShsZWdlbmQucG9zaXRpb24gPSBjKDAuODUsIDAuNzUpKSArIAogIHNjYWxlX3hfY29udGludW91cyhleHBhbmQgPSBjKDAsIDApLCBsaW1pdHMgPSBjKDAsIDUpKSArIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBjKDAsIDApLCBsaW1pdHMgPSBjKDAsIDUpKSArCiAgZ3VpZGVzKGNvbG91ciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KGZpbGwgPSBhbHBoYSgnd2hpdGUnLCAxKSkpKQoKYGBgCgpUaGUgY29tcGxldGUgaW5wdXQgKHgxLCB4MiwgZjEsIGYyKSBvdmVyY29tcGxpY2F0ZXMgdGhlIHN5c3RlbSwgcHJvdmlkaW5nIDggZ3JvdXBzLiBPbmUgb2YgdGhlc2UgZ3JvdXBzIGlzIGNsZWFybHkgdGhlIFBhcmV0byBmcm9udGllciwgYnV0IGl0IGRvZXMgbm90IGluY2x1ZGUgYW55IHBvaW50cyB0aGF0IGFyZSBvZiBzaW1pbGFyIHBlcmZvcm1hbmNlLiBUaGVyZSBhcmUgcG9pbnRzIHRvIGVpdGhlciBzaWRlIG9mIGl0IHN1Z2dlc3Rpbmcgc2ltaWxhciBwZXJmb3JtYW5jZSwgYnV0IGhvdyBmYXIgdGhleSBleHRlbmQgaXMgZGlmZmljdWx0IHRvIGludGVycHJldC4KClRoZSBuZWdhdGl2ZSBjb250cm9sICh4MSwgeDIpIGdpdmVzIHRoZSBleHBlY3RlZCByZXN1bHQgb2YgbGFyZ2VseSBncm91cGluZyBiYXNlZCBvbiBzYW1wbGluZyBkZW5zaXR5LiBUaGlzIG1lYW5zIHRoZSBoaWdobHkgc2FtcGxlZCBQYXJldG8gZnJvbnRpZXIgYW5kIGxvY2FsIG1pbmltdW0gYXJlIHRoZWlyIG93biBncm91cHMsIGFuZCB0aGUgcmVzdCBvZiB0aGUgc3BhY2UgaXMgZGl2aWRlZCBiYXNlZCBhcm91bmQgdGhlIGJvdW5kYXJ5IGJldHdlZW4gdGhlIGhpZ2hseSBzYW1wbGVkIHJlZ2lvbnMuCgpUaGUgb3V0cHV0LW9ubHkgbW9kZWwgZ2l2ZXMgdGhlIG1vc3QgdXNlZnVsIHJlc3VsdHMsIGFzIGl0IGdyb3VwcyB0aGUgUGFyZXRvIGZyb250aWVyIGluc2lkZSBvZiBhbm90aGVyIGhpZ2gtcGVyZm9ybWluZyBncm91cCwgd2hpY2ggYWxzbyBpbmNsdWRlcyB0aGUgbG9jYWwgb3B0aW11bSBhbmQgdGhlIHNwYWNlIHNwYW5uaW5nIHRvIGl0LiBJdCByb3VnaGx5IGxpbmVzIHVwIHdpdGggdGhlIFBhcmV0byBkaXN0YW5jZSBvciB0aHJlc2hvbGQgY3JpdGVyaWE7IGl0IGRvZXMgbm90IGFwcGVhciB0byBwcmlvcml0aXplIHRoZSBhbmdsZSBvciB1dG9waWEgZGlzdGFuY2UuIFNob3dpbmcgb25seSB0aGlzIG1vZGVsIGF0IGZpbmVyIHJlc29sdXRpb24gYXMgaXQgaXMgdGhlIG9ubHkgcmVsZXZhbnQgcGVyZm9ybWluZyBtb2RlbAoKYGBge3J9CnJlcyA9IHByZWRpY3Qob2JqZWN0ID0gY2x1c3Rlci5vdXQsIG5ld2RhdGEgPSBmaW5lLmdyaWRbYygnZjEnLCAnZjInKV0pCmZpbmUuZ3JpZCRjbHVzdGVyLm91dCA9IHJlcyRjbGFzc2lmaWNhdGlvbgpnZ3Bsb3QoKSArCiAgIyBDbHVzdGVyIHJlc3VsdHMKICBnZW9tX3BvaW50KGRhdGEgPSBmaW5lLmdyaWQsIG1hcHBpbmcgPSBhZXMoeCA9IHgxLCB5ID0geDIsIGNvbG9yID0gYXMuZmFjdG9yKGNsdXN0ZXIub3V0KSkpICsKICAjIEJvdW5kYXJpZXM6ICsvLSBzb21lIHNlcGFyYXRpb24gZnJvbSAwLjUKICBnZW9tX2NvbnRvdXIoZGF0YSA9IGZpbmUuZ3JpZCwgbWFwcGluZyA9IGFlcyh4ID0geDEsIHkgPSB4MiwgeiA9IGRpc3QpLCBjb2xvciA9ICdibGFjaycsIGJyZWFrcyA9IGMoMSkpICsKICBnZW9tX2NvbnRvdXIoZGF0YSA9IGZpbmUuZ3JpZCwgbWFwcGluZyA9IGFlcyh4ID0geDEsIHkgPSB4MiwgeiA9IGN1dG9mKSwgY29sb3IgPSAncmVkJywgYnJlYWtzID0gYygwLjUpKSArCgogIGxhYnMoeCA9IGV4cHJlc3Npb24oJ3gnWzFdKSwgeSA9IGV4cHJlc3Npb24oJ3gnWzJdKSwgCiAgICAgICBjb2xvciA9ICdHcm91cCcsIGZpbGwgPSAnR3JvdXAnLCAKICAgICAgIHN1YnRpdGxlID0gJyhmMSwgZjIpJykgKwogIHRoZW1lX2NsYXNzaWMoKSArICN0aGVtZShsZWdlbmQucG9zaXRpb24gPSBjKDAuODUsIDAuNzUpKSArIAogIHNjYWxlX3hfY29udGludW91cyhleHBhbmQgPSBjKDAsIDApLCBsaW1pdHMgPSBjKDAsIDUpKSArIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBjKDAsIDApLCBsaW1pdHMgPSBjKDAsIDUpKSArCiAgZ3VpZGVzKGNvbG91ciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KGZpbGwgPSBhbHBoYSgnd2hpdGUnLCAxKSkpKQoKcmVzID0gcHJlZGljdChvYmplY3QgPSBjbHVzdGVyLmFsbCwgbmV3ZGF0YSA9IGZpbmUuZ3JpZFtjKCd4MScsICd4MicsICdmMScsICdmMicpXSkKZmluZS5ncmlkJGNsdXN0ZXIuYWxsID0gcmVzJGNsYXNzaWZpY2F0aW9uCmdncGxvdCgpICsKICAjIENsdXN0ZXIgcmVzdWx0cwogIGdlb21fcG9pbnQoZGF0YSA9IGZpbmUuZ3JpZCwgbWFwcGluZyA9IGFlcyh4ID0geDEsIHkgPSB4MiwgY29sb3IgPSBhcy5mYWN0b3IoY2x1c3Rlci5hbGwpKSwgc2l6ZSA9IDQpICsKICAjIEJvdW5kYXJpZXM6ICsvLSBzb21lIHNlcGFyYXRpb24gZnJvbSAwLjUKICBnZW9tX2NvbnRvdXIoZGF0YSA9IGZpbmUuZ3JpZCwgbWFwcGluZyA9IGFlcyh4ID0geDEsIHkgPSB4MiwgeiA9IGRpc3QpLCBjb2xvciA9ICdibGFjaycsIGJyZWFrcyA9IGMoMSkpICsKICBnZW9tX2NvbnRvdXIoZGF0YSA9IGZpbmUuZ3JpZCwgbWFwcGluZyA9IGFlcyh4ID0geDEsIHkgPSB4MiwgeiA9IGN1dG9mKSwgY29sb3IgPSAncmVkJywgYnJlYWtzID0gYygwLjUpKSArCgogIGxhYnMoeCA9IGV4cHJlc3Npb24oJ3gnWzFdKSwgeSA9IGV4cHJlc3Npb24oJ3gnWzJdKSwgCiAgICAgICBjb2xvciA9ICdHcm91cCcsIGZpbGwgPSAnR3JvdXAnLCAKICAgICAgIHN1YnRpdGxlID0gJyh4MSwgeDIsIGYxLCBmMiknKSArCiAgdGhlbWVfY2xhc3NpYygpICsgI3RoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC44NSwgMC43NSkpICsgCiAgc2NhbGVfeF9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCksIGxpbWl0cyA9IGMoMCwgNSkpICsgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCksIGxpbWl0cyA9IGMoMCwgNSkpICsKICBndWlkZXMoY29sb3VyID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3QoZmlsbCA9IGFscGhhKCd3aGl0ZScsIDEpKSkpCgpnZ3Bsb3QoKSArCiAgIyBDbHVzdGVyIHJlc3VsdHMKICBnZW9tX3BvaW50KGRhdGEgPSBmaWx0ZXIoZmluZS5ncmlkLCBmMiA8IDUwLCBmMSA8IDIwMCksIG1hcHBpbmcgPSBhZXMoeCA9IGYxLCB5ID0gZjIsIGNvbG9yID0gYXMuZmFjdG9yKGNsdXN0ZXIuYWxsKSksIHNpemUgPSAyKSArCiAgbGFicyh4ID0gZXhwcmVzc2lvbignZidbMV0pLCB5ID0gZXhwcmVzc2lvbignZidbMl0pLCAKICAgICAgIGNvbG9yID0gJ0dyb3VwJywgZmlsbCA9ICdHcm91cCcsIAogICAgICAgc3VidGl0bGUgPSAnKHgxLCB4MiwgZjEsIGYyKScpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChmaWxsID0gYWxwaGEoJ3doaXRlJywgMSkpKSkKCmBgYAoKVGhlIChmMSwgZjIpIG1vZGVsIGFwcGVhcnMgdG8gZ2l2ZSBhIGNsdXN0ZXIgdGhhdCBpcyBzb21ld2hlcmUgYmV0d2VlbiBhIHRocmVzaG9sZCBjdXRvZmYgYW5kIHRoZSBQYXJldG8gZGlzdGFuY2UgY3V0b2ZmLiAKSW4gY29udHJhc3QsIHRoZSBiZWhhdmlvciBvZiB0aGUgKHgxLCB4MiwgZjEsIGYyKSBtb2RlbCBwcm92aWRlcyBtYW55IG1vcmUgZ3JvdXBzLCBpbmNsdWRpbmcgc3BsaXR0aW5nIHRoZSBQYXJldG8gZnJvbnQgaW50byBhYm91dCB0aHJlZSBkaWZmZXJlbnQgY2F0ZWdvcmllcyB3aXRoIG5vIG9idmlvdXMgYW5hbG9nIHRvIHdoeSB0aGUgYm91bmRhcmllcyBhcmUgd2hlcmUgdGhleSBhcmUuIApJdCBhcHBlYXJzIHRvIHJvdWdobHkgZml0IHRoZSBzYW1lIGJvdW5kYXJpZXMgb2YgUGFyZXRvIGRpc3RhbmNlIG9yIHRocmVzaG9sZCBjdXRvZmZzLCBidXQgbm90IHZlcnkgd2VsbC4gCkEgcm91Z2ggYXBwcm94aW1hdGlvbiBpcyB0aGF0IGdyb3VwcyAoNSwgNikgYXJlIHRoZSBQYXJldG8gZnJvbnQsIGdyb3VwcyAoMiwgMywgNykgbWFrZSB1cCB0aGUgcmVnaW9uIGNsb3NlIHRvIHRoZSBQYXJldG8gZnJvbnQsIGFuZCAoMSwgNCwgOCkgYXJlIHRoZSByZWdpb24gZmFyIGZyb20gdGhlIGZyb250LgoKVGhlIG1vZGVsIGZvciAoZjEsIGYyKSBpcyBnb2luZyB0byBnaXZlIHRoZSBzYW1lIGNvbmRpdGlvbmFsIHByb2JhYmlsaXRpZXMgYXMgdGhlIFBhcmV0byBkaXN0YW5jZSBvciB0aHJlc2hvbGQgYWNjZXB0YW5jZSBjcml0ZXJpYSBiYXNlZCBvbiB0aGlzIHNpbWlsYXJpdHkgaW4gdGhlIHNoYXBlIG9mIHRoZSBib3VuZGFyeS4gClRoZSBpbnRlcmVzdGluZyByZXN1bHQgdG8gaW50ZXJwcmV0IGlzIHRoZSAoeDEsIHgyLCBmMSwgZjIpLCBwYXJ0aWN1bGFybHkgd2hlbiBtYXJnaW5hbGl6aW5nIHRvICh4MSwgeDIpLiAKQXNzdW1pbmcgdGhhdCBjYWxjdWxhdGluZyAoZjEsIGYyKSBpcyBleHBlbnNpdmUsIHRoZSBiZXN0IGFwcHJveGltYXRpb24gaXMgdGhhdCBmb3VuZCBmcm9tIHRoZSBHUCBtb2RlbHMgdXNlZCB0byBmaW5kIHRoZSBQYXJldG8gZnJvbnQgaXRzZWxmLiAKVGhlc2Ugd2lsbCBnaXZlIChmMSwgZjIpIGFzIGEgYml2YXJpYXRlIEdhdXNzaWFuIGRpc3RyaWJ1dGlvbiwgd2hpY2ggY2FuIGJlIHNhbXBsZWQgZnJvbSB0byBlc3RpbWF0ZSB0aGUgbGlrZWxpaG9vZCB0aGF0IGl0IGZhbGxzIGludG8gdGhlIFBhcmV0byBmcm9udCwgdGhlIHJlZ2lvbiBjbG9zZSB0byBpdCwgb3IgdGhlIHJlZ2lvbiBmYXIgZnJvbSBpdC4KVGhpcyBpcyBhY2hpZXZhYmxlIHdpdGggYSBzZXF1ZW50aWFsIE1vbnRlIENhcmxvOiBnaXZlbiB4MSwgc2FtcGxlIHgyIGZyb20gdGhlIHJhbmdlLCBmaW5kIHRoZSBkaXN0cmlidXRpb24gb2YgKGYxLCBmMiksIHNhbXBsZSAoZjEsIGYyKSwgYW5kIHNvbHZlIHRoZSBjbGFzc2lmaWNhdGlvbiBpbnRvIHRoZSB0aHJlZSBncm91cHMuCgpgYGB7cn0KZjEubW9kID0gZmlsbC5zYW1wbGUubW9kKEdQYXIuZGF0YSA9IEdQYXIuYWxsLCBpbnB1dC5uYW1lID0gYygneDEnLCAneDInKSwgb3V0cHV0Lm5hbWUgPSAnZjEnKQpmMi5tb2QgPSBmaWxsLnNhbXBsZS5tb2QoR1Bhci5kYXRhID0gR1Bhci5hbGwsIGlucHV0Lm5hbWUgPSBjKCd4MScsICd4MicpLCBvdXRwdXQubmFtZSA9ICdmMicpCgp4LnJuZyA9IHNlcShmcm9tID0gMCwgdG8gPSA1LCBsZW5ndGgub3V0ID0gNTApCm5zYW1wLnggPSAxMDA7IG5zYW1wLmYgPSAxMDAKZ20ubWFyZ2luID0gZGF0YS5mcmFtZSgpCmZvcih4IGluIHgucm5nKXsKICAjIHgxCiAgIyBTYW1wbGUgeDIKICBpbmZyYW1lID0gZGF0YS5mcmFtZSh4MSA9IHgsIHgyID0gcnVuaWYobiA9IG5zYW1wLngsIG1pbiA9IDAsIG1heCA9IDUpKQogIGNsYXNzZXMgPSBjKCkKICBmb3IobiBpbiAxOm5yb3coaW5mcmFtZSkpewogICAgIyBFc3RpbWF0ZSBkaXN0cmlidXRpb24gZm9yIGYxLCBmMgogICAgZjEuZXN0ID0gcHJlZGljdChvYmplY3QgPSBmMS5tb2QsIG5ld2RhdGEgPSBpbmZyYW1lLCB0eXBlID0gJ1VLJykKICAgIGYyLmVzdCA9IHByZWRpY3Qob2JqZWN0ID0gZjIubW9kLCBuZXdkYXRhID0gaW5mcmFtZSwgdHlwZSA9ICdVSycpCiAgICB0ZXN0LmZyYW1lID0gZGF0YS5mcmFtZSh4MSA9IGluZnJhbWUkeDEsIHgyID0gaW5mcmFtZSR4MiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGYxID0gcm5vcm0obiA9IG5zYW1wLmYsIG1lYW4gPSBmMS5lc3QkbWVhbiwgc2QgPSBmMS5lc3Qkc2QpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZjIgPSBybm9ybShuID0gbnNhbXAuZiwgbWVhbiA9IGYyLmVzdCRtZWFuLCBzZCA9IGYyLmVzdCRzZCkpCiAgICByZXMgPSBwcmVkaWN0KG9iamVjdCA9IGNsdXN0ZXIuYWxsLCBuZXdkYXRhID0gdGVzdC5mcmFtZSkKICAgIGNsYXNzZXMgPSBjKGNsYXNzZXMsIHJlcyRjbGFzc2lmaWNhdGlvbikKICB9CiAgY2xhc3NlcyA9IGRhdGEuZnJhbWUoZ3JvdXAgPSBjbGFzc2VzKQogIGdtLm1hcmdpbiA9IHJiaW5kKGdtLm1hcmdpbiwgZGF0YS5mcmFtZSh4ID0geCwgdmFyID0gJ3gxJywgCiAgICAgICAgICAgICAgICAgICAgICAgICBwLnBhcmUgPSBucm93KGZpbHRlcihjbGFzc2VzLCBncm91cCA9PSA1IHwgZ3JvdXAgPT0gNikpLyhuc2FtcC5mKm5zYW1wLngpLCAKICAgICAgICAgICAgICAgICAgICAgICAgIHAubmVhciA9IG5yb3coZmlsdGVyKGNsYXNzZXMsIGdyb3VwID09IDIgfCBncm91cCA9PSAzIHwgZ3JvdXAgPT0gNykpLyhuc2FtcC5mKm5zYW1wLngpLCAKICAgICAgICAgICAgICAgICAgICAgICAgIHAuZGlzdCA9IG5yb3coZmlsdGVyKGNsYXNzZXMsIGdyb3VwID09IDEgfCBncm91cCA9PSA0IHwgZ3JvdXAgPT0gOCkpLyhuc2FtcC5mKm5zYW1wLngpKSkKICAjIHgyCiAgIyBTYW1wbGUgeDIKICBpbmZyYW1lID0gZGF0YS5mcmFtZSh4MiA9IHgsIHgxID0gcnVuaWYobiA9IG5zYW1wLngsIG1pbiA9IDAsIG1heCA9IDUpKQogIGNsYXNzZXMgPSBjKCkKICBmb3IobiBpbiAxOm5yb3coaW5mcmFtZSkpewogICAgIyBFc3RpbWF0ZSBkaXN0cmlidXRpb24gZm9yIGYxLCBmMgogICAgZjEuZXN0ID0gcHJlZGljdChvYmplY3QgPSBmMS5tb2QsIG5ld2RhdGEgPSBpbmZyYW1lLCB0eXBlID0gJ1VLJykKICAgIGYyLmVzdCA9IHByZWRpY3Qob2JqZWN0ID0gZjIubW9kLCBuZXdkYXRhID0gaW5mcmFtZSwgdHlwZSA9ICdVSycpCiAgICB0ZXN0LmZyYW1lID0gZGF0YS5mcmFtZSh4MSA9IGluZnJhbWUkeDEsIHgyID0gaW5mcmFtZSR4MiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGYxID0gcm5vcm0obiA9IG5zYW1wLmYsIG1lYW4gPSBmMS5lc3QkbWVhbiwgc2QgPSBmMS5lc3Qkc2QpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZjIgPSBybm9ybShuID0gbnNhbXAuZiwgbWVhbiA9IGYyLmVzdCRtZWFuLCBzZCA9IGYyLmVzdCRzZCkpCiAgICByZXMgPSBwcmVkaWN0KG9iamVjdCA9IGNsdXN0ZXIuYWxsLCBuZXdkYXRhID0gdGVzdC5mcmFtZSkKICAgIGNsYXNzZXMgPSBjKGNsYXNzZXMsIHJlcyRjbGFzc2lmaWNhdGlvbikKICB9CiAgY2xhc3NlcyA9IGRhdGEuZnJhbWUoZ3JvdXAgPSBjbGFzc2VzKQogIGdtLm1hcmdpbiA9IHJiaW5kKGdtLm1hcmdpbiwgZGF0YS5mcmFtZSh4ID0geCwgdmFyID0gJ3gyJywgCiAgICAgICAgICAgICAgICAgICAgICAgICBwLnBhcmUgPSBucm93KGZpbHRlcihjbGFzc2VzLCBncm91cCA9PSA1IHwgZ3JvdXAgPT0gNikpLyhuc2FtcC5mKm5zYW1wLngpLCAKICAgICAgICAgICAgICAgICAgICAgICAgIHAubmVhciA9IG5yb3coZmlsdGVyKGNsYXNzZXMsIGdyb3VwID09IDIgfCBncm91cCA9PSAzIHwgZ3JvdXAgPT0gNykpLyhuc2FtcC5mKm5zYW1wLngpLCAKICAgICAgICAgICAgICAgICAgICAgICAgIHAuZGlzdCA9IG5yb3coZmlsdGVyKGNsYXNzZXMsIGdyb3VwID09IDEgfCBncm91cCA9PSA0IHwgZ3JvdXAgPT0gOCkpLyhuc2FtcC5mKm5zYW1wLngpKSkKfQoKd3JpdGUuY3N2KGdtLm1hcmdpbiwgJ01hcmdpbl9HTS5jc3YnKQpgYGAKCmBgYHtyfQpnbS5tYXJnaW4gPSByZWFkLmNzdignTWFyZ2luX0dNLmNzdicpCmdtLm1hcmdpbiA9IGRhdGEuZnJhbWUoeCA9IHJlcChnbS5tYXJnaW4keCwgMyksCiAgICAgICAgICAgICAgICAgICAgICAgdmFyID0gcmVwKGdtLm1hcmdpbiR2YXIsIDMpLAogICAgICAgICAgICAgICAgICAgICAgIHByb2IgPSBjKGdtLm1hcmdpbiRwLnBhcmUsIGdtLm1hcmdpbiRwLm5lYXIsIGdtLm1hcmdpbiRwLmRpc3QpLAogICAgICAgICAgICAgICAgICAgICAgIHR5cCA9IGMocmVwKCdQYXJldG8nLCBucm93KGdtLm1hcmdpbikpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcCgnTmVhci1QYXJldG8nLCBucm93KGdtLm1hcmdpbikpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcCgnU3Vib3B0aW1hbCcsIG5yb3coZ20ubWFyZ2luKSkpKQpnZ3Bsb3QoZ20ubWFyZ2luKSArCiAgZ2VvbV9wYXRoKG1hcHBpbmcgPSBhZXMoeCA9IHgsIHkgPSBwcm9iLCBjb2xvciA9IHR5cCkpICsKICBmYWNldF93cmFwKH52YXIsIG5yb3cgPSAyKSArCiAgc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGUgPSAnRGFyazInKSArIAogIGxhYnMoeCA9ICdJbnB1dCBWYWx1ZScsIHkgPSAnQ29uZGl0aW9uYWwgUHJvYmFiaWxpdHknLCBjb2xvciA9ICdSZWdpb24nKQoKZ20ubWFyZ2luID0gcmVhZC5jc3YoJ01hcmdpbl9HTS5jc3YnKQpnbS5tYXJnaW4gPSBkYXRhLmZyYW1lKHggPSByZXAoZ20ubWFyZ2luJHgpLAogICAgICAgICAgICAgICAgICAgICAgIHZhciA9IHJlcChnbS5tYXJnaW4kdmFyKSwKICAgICAgICAgICAgICAgICAgICAgICBwcm9iID0gZ20ubWFyZ2luJHAucGFyZSArIGdtLm1hcmdpbiRwLm5lYXIsCiAgICAgICAgICAgICAgICAgICAgICAgdHlwID0gJ09wdGltYWwnKQpnZ3Bsb3QoZ20ubWFyZ2luKSArCiAgZ2VvbV9wYXRoKG1hcHBpbmcgPSBhZXMoeCA9IHgsIHkgPSBwcm9iLCBjb2xvciA9IHR5cCkpICsKICBmYWNldF93cmFwKH52YXIsIG5yb3cgPSAyKSArCiAgc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGUgPSAnRGFyazInKSArIAogIGxhYnMoeCA9ICdJbnB1dCBWYWx1ZScsIHkgPSAnQ29uZGl0aW9uYWwgUHJvYmFiaWxpdHknLCBjb2xvciA9ICdSZWdpb24nKQoKYGBgCgpUaGUgZXN0aW1hdGVkIHByb2JhYmlsaXR5IG9mIGJlaW5nIG9wdGltYWwgKFBhcmV0byBmcm9udCBncm91cCBvciB0aGUgbmVhci1QYXJldG8gZ3JvdXApIGlzIHNpbWlsYXIgdG8gdGhhdCBjYWxjdWxhdGVkIHRocm91Z2ggdGhlIG90aGVyIG1ldGhvZHMgZm9yIHRoZSBQYXJldG8gZGlzdGFuY2Ugb3IgdGhyZXNob2xkIGNyaXRlcmlhLiBIb3dldmVyLCBiZWNhdXNlIG9mIHRoZSBudW1lcm91cyB2YXJpYWJsZXMgcmVxdWlyZWQgdG8gZ2VuZXJhdGUgdGhlIG1vZGVsLCBhIGxhcmdlciBhbmQgbW9yZSBjb21wbGljYXRlZCBzYW1wbGluZyBwcm9jZWR1cmUgaXMgbmVjZXNzYXJ5IGZvciBhY2N1cmF0ZSBlc3RpbWF0ZXMuCgoKIyBPdmVyYWxsOgoqIFRoZSB1bnN1cGVydmlzZWQgTUwgbW9kZWwgKEdhdXNzaWFuIG1peHR1cmUpIGNhbiBvbmx5IHRlbmQgdG93YXJkcyB0aGUgY3JpdGVyaWEgb2YgaGF2aW5nIG91dHB1dCB2YWx1ZXMgc2ltaWxhciB0byB0aGF0IG9mIHRoZSBQYXJldG8gZnJvbnQuIEhvd2V2ZXIsIGluIG9yZGVyIHRvIG9idGFpbiB0aGUgbW9kZWwsIGFsbCBvZiB0aGUgaW5wdXRzIGFuZCBvdXRwdXRzIGFyZSB1c2VkLCBtZWFuaW5nIHRvIHNvbHZlIHRoZSBzaW5nbGUtdmFyaWFibGUgcHJvYmFiaWxpdGllcywgb25lIG11c3QgcGVyZm9ybSBhIHRpbWUtY29uc3VtaW5nIG5lc3RlZCBzYW1wbGluZyBwcm9jZWR1cmUuCiogVGhlIHN1cGVydmlzZWQgTUwgbW9kZWwgKFN1cHBvcnQgVmVjdG9yIE1hY2hpbmVzKSBhcmUgZmxleGlibGUgdG8gc2VsZWN0aW9uIGNyaXRlcmlhIGJlY2F1c2UgdGhvc2UgYXJlIHBhcnQgb2YgdGhlIHVzZXIgaW5wdXQgaW50byBnZW5lcmF0aW5nIHRoZSBtb2RlbC4gSXQgaXMgdmVyeSBzZW5zaXRpdmUgdG8gdGhlIGtlcm5lbCBmdW5jdGlvbiB0aGF0IHdhcyBzZWxlY3RlZCwgd2l0aCB0aGUgcmFkaWFsIGFuZCBwb2x5bm9taWFsIGZvcm1zIHdvcmtpbmcgYmVzdCBmb3IgdGhpcyBzcGVjaWZpYyBjYXNlLiBXaGVuIGNvbXBhcmVkIHRvIHRoZSBhY3R1YWwgZnVuY3Rpb24sIHRoZXNlIGdvb2QtZml0dGluZyBrZXJuZWxzIGFwcGVhcmVkIHRvIGJlIG92ZXItZml0IHRvIHRoZSBkYXRhIGFuZCBjYW5ub3QgY2FwdHVyZSB0aGUgZnVsbCBkeW5hbWljcyBvZiB0aGUgc3BhY2UuIEluIHByYWN0aWNlLCBzZXBhcmF0ZSB0cmFpbmluZyBhbmQgdGVzdGluZyBzZXRzIHNob3VsZCBiZSB1c2VkLCB3aGljaCBtYXkgYmUgZGlmZmljdWx0IGZvciBtb3JlIGNvbXBsZXggb2JqZWN0aXZlIGZ1bmN0aW9ucyBkdWUgdG8gbW9yZSBmdW5jdGlvbiBldmFsdWF0aW9ucy4gCiogVGhlIEdQIG1ldGhvZCByZXF1aXJlcyBpdGVyYXRpdmUgc2FtcGxpbmcsIGFuZCB0aGVyZWZvcmUgcmVxdWlyZXMgbW9yZSB0aW1lIHRvIGNvbXB1dGUgaW5pdGlhbGx5LiBIb3dldmVyLCBpdCBhY2NvdW50cyBmb3IgdGhlIHVuY2VydGFpbnR5IGluIHRoZSBtb2RlbCBleHBsaWNpdGx5LCB3aGVyZWFzIHRoZSBTVk0gbWV0aG9kcyByZWx5IHB1cmVseSBvbiBmcmVxdWVudGlzdCBzYW1wbGluZy4gVGhpcyBtZWFucyB0aGUgc2luZ2xlLXZhcmlhYmxlIHByb2JhYmlsaXRpZXMgZnJvbSB0aGUgR1AgbW9kZWwgZXN0aW1hdGUgYXJlIGdlbmVyYWxseSBsb3dlciB0aGFuIHRob3NlIGVzdGltYXRlZCBmcm9tIHRoZSBTVk0uCiogTW9yZSBjb21wbGljYXRlZCBjb25kaXRpb25zLCBzdWNoIGFzIG9ubHkgYWNjZXB0aW5nIHBvaW50cyB0aGF0IHByaW9yaXRpemUgb25lIG9iamVjdGl2ZSBvdmVyIHRoZSBvdGhlciBieSBhIGNlcnRhaW4gbWFyZ2luLCBhcmUgZGlmZmljdWx0IGZvciBib3RoIHN1cGVydmlzZWQgbGVhcm5pbmcgbWV0aG9kcyB0byBlc3RpbWF0ZSwgYnV0IHRoZSByZXN1bHRzIGZyb20gdGhlIFNWTSBhcHBlYXIgdG8gYmUgd29yc2UgY29tcGFyZWQgdG8gdGhlIGFjdHVhbCBib3VuZGFyaWVzLgoqIFRoZSBTVk0gbWV0aG9kcyBnaXZlIHNpbmdsZS12YXJpYWJsZSBwcm9iYWJpbGl0aWVzIHRoYXQgYXJlIHNpbWlsYXIgdG8gdGhlIGV4cGVjdGVkIG1hcmdpbmFsLCBidXQgbm90IGFzIGNsb3NlIGFzIHRoZSBHUCBtZXRob2QuCgo=